URL: https://www.opennet.me/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 8828
[ Назад ]

Исходное сообщение
"Интерфейс для доступа к профайлу"

Отправлено dimonna , 10-Авг-10 17:28 
Риспект всем форумчанам!

Существуют ли более-менее стандартные шаблоны проектирования для загрузки/сохранения данных из/в профайла примерно такого синтаксиса.

сохранение:
void saveValueToProfile(const char* const name, const T& value);

загрузка:
const T value = loadValueFromProfile(const char* const name);

Здесь name указывает значение какой переменной из профайла представлено значением value типа T соответствующего этой переменной.

Вот пример профайла:

Переменная1 = 10
Переменная2 = 3,141592645
Переменная3 = Балалайка

то есть типы данных разношерстные, но соответствующие заданным в коде типам данных
Переменная1 -> int
Переменная2 -> double
Переменная3 -> char*

Кто и как решал подобные задачи?

С уважением,
Дмитрий


Содержание

Сообщения в этом обсуждении
"Интерфейс для доступа к профайлу"
Отправлено dimonna , 10-Авг-10 17:30 
Ах, да, функции загрузки сохранения значений конечно могут быть параметризованными типом данных значения

"Интерфейс для доступа к профайлу"
Отправлено аноним , 10-Авг-10 21:24 
Что такое профайл? Какая структура данных? Для того, что ты написал, используются обычные C++ потоки.

"Интерфейс для доступа к профайлу"
Отправлено dimonna , 11-Авг-10 08:22 
>Что такое профайл? Какая структура данных? Для того, что ты написал, используются
>обычные C++ потоки.

Это файл примерно такой структуры:
Переменная1 = Значение1
Переменная2 = Значение2
Переменная3 = Значение3
...

Загрузка-выгрузка данных не пробоема, задача - интерфейс в сторону клиентского кода


"Интерфейс для доступа к профайлу"
Отправлено pilot.net , 11-Авг-10 07:45 
Я бы так сделал

class Preferences {
   // group settings
    const string& getGroup();
    void setGroup(const string& group);

    // boolean data storage
    bool getBool(const QString& key, bool def = false);
    void setBool(const QString& key, bool value);
    // integer data storage
    long getNumber(const QString& key, long def = 0);
    void setNumber(const QString& key, long value);
    // double data storage
    double getDouble(const QString& key, double def = 0.0);
    void setDouble(const QString& key, double value);
    // string data storage
    QString getString(const QString& key, const QString& def = "NULL");
    void setString(const QString& key, const QString& value);
    // remove a key/value from the preferences
    void removeKey(const QString& key);
    // remove the current group from the preferences
    void removeGroup();

но есть еще варианты


"Интерфейс для доступа к профайлу"
Отправлено dimonna , 11-Авг-10 08:28 
>Я бы так сделал ...

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


"Интерфейс для доступа к профайлу"
Отправлено pilot.net , 11-Авг-10 08:37 
Ну еще вариант
class Preferences {

сохранение:
void set(const sting & name, const T& value);

загрузка:
bool err  = get(const string &name , T& value);


"Интерфейс для доступа к профайлу"
Отправлено dimonna , 11-Авг-10 09:38 
>Ну еще вариант
>class Preferences {

...

Уже лучше :), только тогда нужен какой-то класс организующий коллекцию таких вот шаблонных классов, и упираемся в вопрос, а как же организовать коллекцию таких вот неоднородных типов данных в этом классе чтобы снова не хардкодить?  Ятак полагаю надо все Preferences унаследовать от какого-нибудь базового класса чтобы иметь чтото типа:

PrefBase* prefs[] =
{
   new Preferences<int>("var1", 123),
   new Preferences<double>("var2", 3.1415926),
   new Preferences<char*>("var3", "test"),
};

Потом у этого класса - менеджера должен быть какой-то метод set и get. Вот тут и начинается самое интересное чтобы как-то организовать такой вот синтаксис:

int var1 = get<int>("var1");
double var2 = get<double>("var2");
const char* var3 = get<char*>("var3");

слудовательно метод get должен выглядеть типа

template<class T> T get(const char* const pVarName)
{
// тут чтото надо нашаманить
}


Вот что тут можно подшаманить чтобы не хардкодить?


"Интерфейс для доступа к профайлу"
Отправлено аноним , 11-Авг-10 10:24 
Берите protocol buffers, или какую-нибудь простую библиотеку для работы с ini-style конфигами.

"Интерфейс для доступа к профайлу"
Отправлено dimonna , 11-Авг-10 11:03 
Вот моя реализация примера:

#include <stdio.h>
#include <string.h>

class Base
{
public:
   Base(const char* const pName):
      mpName(pName)
   {
   }

   inline const char* getName() const
   {
      return mpName;
   }

private:
   const char* const mpName;
};

template<class T> class Value : public Base
{
public:
   explicit Value(const char* const pName, const T& value):
      Base(pName),
      mValue(value)
   {
   }

   const T& getValue() const
   {
      return mValue;
   }

private:
   const T mValue;
};

/* DO NOT CHANGE */
/* DO NOT CHANGE */const Base* const vars[] =
/* DO NOT CHANGE */{
/* DO NOT CHANGE */   new Value<int>("variable1", 123),
/* DO NOT CHANGE */   new Value<double>("variable2", 3.141592645),
/* DO NOT CHANGE */   new Value<char*>("variable3", "dima")
/* DO NOT CHANGE */};
/* DO NOT CHANGE */

template<class T> T getValue(const char* const pName)
{
   for (unsigned i = 0; sizeof vars / sizeof vars[0] > i; ++i)
   {
      const Base* const pVar = vars[i];
      if (0 == strcmp(pName, pVar->getName()))
      {
         const Value<T>* const pValue = static_cast<const Value<T>*>(pVar);
         return pValue->getValue();
      }
   }

   return 0;
}

int main(int argc, char *argv[])
{
   int var1 = getValue<int>("variable1");
   printf("var1 = %d\n", var1);

   int var2_int = getValue<int>("variable2");
   printf("var2_int = %d\n", var2_int);

   double var2_double = getValue<double>("variable2");
   printf("var2_double = %f\n", var2_double);

   const char* const var3 = getValue<char*>("variable3");
   printf("var3 = %s\n", var3);

   return 0;
}


Как видите здесь var2_int приведена для примера неправильного использования типа данных, поскольку variable2 имеет тип double а не int

Как сделать проверку, чтобы этот пример не компилился из-за var2_int ?


"Интерфейс для доступа к профайлу"
Отправлено аноним , 11-Авг-10 13:56 
Берите protocol buffers, или какую-нибудь простую библиотеку для работы с ini-style конфигами.

"Интерфейс для доступа к профайлу"
Отправлено pilot.net , 11-Авг-10 14:38 
>Вот моя реализация примера:

Зачем столько геморою из-за таких мелочей
тем более ты зарание знаешь какой тип у mValue

>Как видите здесь var2_int приведена для примера неправильного использования типа данных, поскольку variable2 имеет тип double а не int

как ты на стадии компиляции определишь что у тебя в ini файле записан double a не int ?


"Интерфейс для доступа к профайлу"
Отправлено dimonna , 11-Авг-10 14:43 
>как ты на стадии компиляции определишь что у тебя в ini файле
>записан double a не int ?

Вот эти записи определяют какой тип данных лежит в профайле и дефолтовое значение для каждой переменной.

/* DO NOT CHANGE */   new Value<int>("variable1", 123),
/* DO NOT CHANGE */   new Value<double>("variable2", 3.141592645),
/* DO NOT CHANGE */   new Value<char*>("variable3", "dima")

Проверка типа данных нужна чтобы избежать неправильного использования интерфейса, как в примере с var2_int

Компайлер тоже знает какая переменная какого типа, но при помещении этих в переменных в массив такая информация теряется поскольку все шаблонные элементы массива берутся по базовому классу.


"Интерфейс для доступа к профайлу"
Отправлено pilot.net , 12-Авг-10 07:47 
>
>/* DO NOT CHANGE */   new Value<int>("variable1", 123),
>/* DO NOT CHANGE */   new Value<double>("variable2", 3.141592645),
>/* DO NOT CHANGE */   new Value<char*>("variable3", "dima")
>
>Проверка типа данных нужна чтобы избежать неправильного использования интерфейса, как в примере
>с var2_int

Используй библиотеку boost там есть такой класс any
http://www.boost.org/doc/libs/1_43_0/doc/html/boost/any.html


"как-то так"
Отправлено Вова , 12-Авг-10 23:29 
>[оверквотинг удален]
>Переменная2 = 3,141592645
>Переменная3 = Балалайка
>
>то есть типы данных разношерстные, но соответствующие заданным в коде типам данных
>
>Переменная1 -> int
>Переменная2 -> double
>Переменная3 -> char*
>
>Кто и как решал подобные задачи?

Class Data{
Data(int);
Data(string);
Data(double);

operator int();
operator string();
operator double();
}

Data get(string parameter_name, Data default_value);

int count = get("variable1", 0);
double value = get("variable2", 3.14);
string name = get("name", "default");


"как-то так"
Отправлено dimonna , 12-Авг-10 23:40 
Хмм, но это ведь не спасет от:

Data d((int)10);
double wrong = d("name"...);

было интом - стало даблом.


"как-то так"
Отправлено Вова , 12-Авг-10 23:45 
>Хмм, но это ведь не спасет от:
>
>Data d((int)10);
>double wrong = d("name"...);
>
>было интом - стало даблом.

Согласен, не спасёт - на этапе компиляции не спасёт.  Логгирование/бросание исключений и тп  рантайм - вполне достаточное решение.


"как-то так"
Отправлено pilot.net , 13-Авг-10 07:38 
>>Хмм, но это ведь не спасет от:
>>
>>Data d((int)10);
>>double wrong = d("name"...);
>>
>>было интом - стало даблом.
>
>Согласен, не спасёт - на этапе компиляции не спасёт.  Логгирование/бросание исключений
>и тп  рантайм - вполне достаточное решение.

этот код законен и ты не как не определишь ошибку ни в стадии компиляции ни в рантайме


"Никогда не говори 'никогда'"
Отправлено Вова , 13-Авг-10 13:28 
>[оверквотинг удален]
>>>Data d((int)10);
>>>double wrong = d("name"...);
>>>
>>>было интом - стало даблом.
>>
>>Согласен, не спасёт - на этапе компиляции не спасёт.  Логгирование/бросание исключений
>>и тп  рантайм - вполне достаточное решение.
>
>этот код законен и ты не как не определишь ошибку ни в
>стадии компиляции ни в рантайме

class Data{
int data_type;

Data(int v):data_type(TYPE_INT){
storage.int = v;
}
...

operator double()
{
  if(data_type != TYPE_DOUBLE)
    ...
}


"Никогда не говори 'никогда'"
Отправлено pilot.net , 13-Авг-10 13:46 
>[оверквотинг удален]
>int data_type;
>
>Data(int v):data_type(TYPE_INT){
>storage.int = v;
>}
>...
>
>operator double()
>{
>  if(data_type != TYPE_DOUBLE)

а если я так вызову  double var = (double)(int)get("dfgdfg")


"Никогда не говори 'никогда'"
Отправлено dimonna , 13-Авг-10 16:00 
Вот вот. Ооочень хочется компайл-тайм проверку. Рантайм проверка чревата тяжело обнаружаемыми ошибками, особенно если какой-то сильно заковыристый тест кейз.

"Никогда не говори 'никогда'"
Отправлено dimonna , 13-Авг-10 16:02 
Ну и даже проблема не в этом:
>> а если я так вызову  double var = (double)(int)get("dfgdfg")

Таким образом надо хардкодить get-метод для каждого типа данных, что не есть гуд как по мне.


"в том-то и дело, что не нужно писать get() для всех типов"
Отправлено Вова , 13-Авг-10 17:24 
get()  - один для всех:
Data get(string parameter_name, Data default_val)
{
    static map <string, Data> config;
   if(map<>::iterator it = config.find(parameter_name) != config.end())
       reutrn it->second;
   return default_val;
}

и спокойно работаешь:
int i = get("counter", 0);
double d = get("pi", 3.14);
string name = get("username", "pupkin");

вызвал int i = get("username"), получил исключение/лог; вызвал int i = get("counter");, потом double d = i (как вы в примере выше) - сам себе злой буратино. Да, можно даблы инициировать интами, мир неидеален, смиритесь.