#c #arrays #class #templates #generic-type-argument
#c #массивы #класс #шаблоны #аргумент универсального типа
Вопрос:
У меня возникли проблемы с некоторыми функциями, которые я хочу реализовать в своем классе config, у меня есть некоторые значения с разными типами, и я хочу сохранить их в map. ранее я использовал класс ExtendedString для хранения всех данных и преобразования их с помощью функции шаблона в требуемый тип, когда мне было нужно. а затем в моем классе конфигурации был map<ExtendedString,vector<ExtendedString> >
для хранения всех ключей в файле конфигурации. вот как уже выглядит мой класс ExtendedString:
class ExtendedString : public std::string
{
public:
ExtenedString() : public std::string
{
}
template <class T> ExtenededString(T)
{
std::stringstream s;
*this = s.str();
}
template <class T>T as(){
istringstream s(*this);
T temp;
d >> temp;
return temp;
}
}
теперь я хочу проанализировать все значения при загрузке моего конфигурационного файла, а также иметь возможность преобразования в любой возможный тип в случае необходимости. например, если я преобразовал некоторое значение в int и в моей среде выполнения мне понадобилось это значение в виде строки, я хочу иметь возможность сразу же его преобразовать, и, кроме того, я предпочитаю не использовать boost в этом случае, это один из моих базовых классов, и я не хочу привязывать boost к каждому проекту, который я разрабатываю.
Комментарии:
1. Я только что купил эту отличную бензопилу. Может кто-нибудь сказать мне, как снять защиту и приклеить предохранительный выключатель?
2. ha? что это значит? вы хотите сказать, что мой вопрос неуместен?
3. Вы не должны наследовать от стандартных библиотечных классов. Я не вижу в вашем
ExtendedString
классе ничего, что вообще должно быть в классе, просто используйте свободные функции.4. @GMan пытаюсь проверить, может ли ваша идея (или, по крайней мере, к чему привела меня ваша подсказка) быть реализована
5. Я имею в виду, что вы, в лучшем случае, отключаете функции, которые существуют для предотвращения сбоев. В худшем случае вы запрашиваете код, обладающий интеллектом почти человеческого уровня — как еще он может решить во время выполнения, как преобразовать, например,
set<DateAndTime>
вmap<string, Complex>
?
Ответ №1:
Одним из классических решений является boost::any
, или массивный union
. Но я думаю, что оба этих подхода вызывают больше проблем, чем решают. Что плохого в создании, скажем, Config
класса, который имеет член для каждого параметра конфигурации?
Комментарии:
1. можете ли вы рассказать мне, как
boost::any
это реализовано?2. @Gajet: boost имеет открытый исходный код, вот реализация
any
: boost.org/doc/libs/1_46_1/boost/any.hpp3. »
boost::any
» в ответе — это ссылка на документацию к библиотеке. »boost::any
» — это, по сути, большой массив,union
который включает в себя как примитивные типы, так иvoid*
. Если вы пытаетесь сохранить значение в a,boost::any
которое не является примитивным типом (скажем, astd::string
), значение присваивается с помощьюnew
, а указатель на это значение сохраняется вunion
. Вы должны отслеживать, что вы помещаете вboost::any
объект, чтобы вы могли правильно преобразовать его обратно.4. Мне не нравится выделение памяти, потому что это медленно, и этого можно было бы избежать. И мне не нравится концепция объекта, который может содержать значения любого типа, при этом программист отвечает за запоминание того, что было сохранено последним.
Ответ №2:
Я бы рекомендовал использовать boost::any
, поскольку похоже, что он делает то, что вы ищете. Однако, если вы действительно не хотите это использовать, вы могли бы справиться с этим примерно так. Возможно, это не лучший способ сделать это (это первое, что пришло мне в голову) — в основном это сохранение значения в том типе, который у вас был изначально, и в виде строки. Если вы попытаетесь использовать get
его в качестве исходного типа, он вернет это значение, в противном случае он будет использовать std::stringstream
, чтобы попытаться преобразовать его.
Примечание: Это не обрабатывает копирование / присвоение, измените d_data
на ваш общий указатель по выбору, чтобы справиться с этим.
#include <string>
#include <sstream>
#include <iostream>
class ConfigValue
{
class HolderBase
{
protected:
virtual void writeValueToStream(std::ostreamamp; os) const = 0;
public:
virtual ~HolderBase() { }
template <class Type>
Type getAs() const
{
std::stringstream ss;
writeValueToStream(ss);
Type temp;
ss >> temp;
return temp;
}
};
template <class Type>
class Holder : public HolderBase
{
Type d_value;
protected:
void writeValueToStream(std::ostreamamp; os) const
{
os << d_value;
}
public:
Holder(const Typeamp; value)
: d_value(value)
{
std::ostringstream oss;
oss << value;
}
Type get() const
{
return d_value;
}
};
HolderBase *d_data;
public:
template <class Type>
ConfigValue(const Typeamp; value)
: d_data(new Holder<Type>(value))
{
}
~ConfigValue()
{
delete d_data;
}
template <class Type>
Type get() const
{
if (Holder<Type> *holder = dynamic_cast<Holder<Type>*>(d_data))
{
return holder->get();
}
else
{
return d_data->getAs<Type>();
}
}
};
int main()
{
ConfigValue cv = 10;
std::cout << cv.get<std::string>() << std::endl;
return 0;
}
Комментарии:
1. ваша реализация — это почти лучшее, что пришло мне в голову, за исключением того, считаете ли вы возможным удалить это строковое значение из каждого типа? и, кстати, boost::any не предоставляет никакой функции преобразования, если типы не совпадают, поэтому это не ответ на мое решение.
2. @Gajet — Я отредактировал код, чтобы удалить
std::string
. Теперь, если вы попытаетесь получить его как тип, отличный от типа, в котором хранятся данные, он вызовет anoperator<<
для записи его в astd::stringstream
, затемoperator>>
для преобразования его в целевой тип. Это то, что вы имели в виду?3. Не забывайте о проблеме копирования, вам нужно либо использовать общий указатель для
d_data
, либо реализовать конструктор копирования иoperator==
. В противном случае вы столкнетесь с проблемами, используя это в контейнере STL.
Ответ №3:
Вместо того, чтобы хранить их все в виде строк, вы могли бы написать класс «Value», а затем написать подклассы для каждого вида значений.
Комментарии:
1. для этого мне нужно использовать функцию виртуального шаблона (чтобы иметь то, что functino делает сейчас), и это невозможно если вы знаете какой-либо лучший способ реализовать все функции, которые я просил, пожалуйста, предоставьте свою реализацию.