как я могу хранить разные типы значений в массиве, используя c ?

#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.hpp

3. » boost::any » в ответе — это ссылка на документацию к библиотеке. » boost::any » — это, по сути, большой массив, union который включает в себя как примитивные типы, так и void* . Если вы пытаетесь сохранить значение в a, boost::any которое не является примитивным типом (скажем, a std::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 . Теперь, если вы попытаетесь получить его как тип, отличный от типа, в котором хранятся данные, он вызовет an operator<< для записи его в a std::stringstream , затем operator>> для преобразования его в целевой тип. Это то, что вы имели в виду?

3. Не забывайте о проблеме копирования, вам нужно либо использовать общий указатель для d_data , либо реализовать конструктор копирования и operator== . В противном случае вы столкнетесь с проблемами, используя это в контейнере STL.

Ответ №3:

Вместо того, чтобы хранить их все в виде строк, вы могли бы написать класс «Value», а затем написать подклассы для каждого вида значений.

Комментарии:

1. для этого мне нужно использовать функцию виртуального шаблона (чтобы иметь то, что functino делает сейчас), и это невозможно если вы знаете какой-либо лучший способ реализовать все функции, которые я просил, пожалуйста, предоставьте свою реализацию.