Риспект всем форумчанам!Существуют ли более-менее стандартные шаблоны проектирования для загрузки/сохранения данных из/в профайла примерно такого синтаксиса.
сохранение:
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*Кто и как решал подобные задачи?
С уважением,
Дмитрий
Ах, да, функции загрузки сохранения значений конечно могут быть параметризованными типом данных значения
Что такое профайл? Какая структура данных? Для того, что ты написал, используются обычные C++ потоки.
>Что такое профайл? Какая структура данных? Для того, что ты написал, используются
>обычные C++ потоки.Это файл примерно такой структуры:
Переменная1 = Значение1
Переменная2 = Значение2
Переменная3 = Значение3
...Загрузка-выгрузка данных не пробоема, задача - интерфейс в сторону клиентского кода
Я бы так сделал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();но есть еще варианты
>Я бы так сделал ...Как вариант захардкодить... а если типов данных много то разрастается колво интерфейсов и юзер может в конечном итоге подумать что запросить получение значения Переменной1 через интерфейс не соответствующий типу данных этой переменной что может привести к потере данных. Желательно строгая типизация данных проверяемая компилятором (возможно шаблоны).
Ну еще вариант
class Preferences {сохранение:
void set(const sting & name, const T& value);загрузка:
bool err = get(const string &name , T& value);
>Ну еще вариант
>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)
{
// тут чтото надо нашаманить
}
Вот что тут можно подшаманить чтобы не хардкодить?
Берите protocol buffers, или какую-нибудь простую библиотеку для работы с ini-style конфигами.
Вот моя реализация примера:#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 ?
Берите protocol buffers, или какую-нибудь простую библиотеку для работы с ini-style конфигами.
>Вот моя реализация примера:Зачем столько геморою из-за таких мелочей
тем более ты зарание знаешь какой тип у mValue>Как видите здесь var2_int приведена для примера неправильного использования типа данных, поскольку variable2 имеет тип double а не int
как ты на стадии компиляции определишь что у тебя в ini файле записан double a не int ?
>как ты на стадии компиляции определишь что у тебя в 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
Компайлер тоже знает какая переменная какого типа, но при помещении этих в переменных в массив такая информация теряется поскольку все шаблонные элементы массива берутся по базовому классу.
>
>/* 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
>[оверквотинг удален]
>Переменная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");
Хмм, но это ведь не спасет от:Data d((int)10);
double wrong = d("name"...);было интом - стало даблом.
>Хмм, но это ведь не спасет от:
>
>Data d((int)10);
>double wrong = d("name"...);
>
>было интом - стало даблом.Согласен, не спасёт - на этапе компиляции не спасёт. Логгирование/бросание исключений и тп рантайм - вполне достаточное решение.
>>Хмм, но это ведь не спасет от:
>>
>>Data d((int)10);
>>double wrong = d("name"...);
>>
>>было интом - стало даблом.
>
>Согласен, не спасёт - на этапе компиляции не спасёт. Логгирование/бросание исключений
>и тп рантайм - вполне достаточное решение.этот код законен и ты не как не определишь ошибку ни в стадии компиляции ни в рантайме
>[оверквотинг удален]
>>>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)
...
}
>[оверквотинг удален]
>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")
Вот вот. Ооочень хочется компайл-тайм проверку. Рантайм проверка чревата тяжело обнаружаемыми ошибками, особенно если какой-то сильно заковыристый тест кейз.
Ну и даже проблема не в этом:
>> а если я так вызову double var = (double)(int)get("dfgdfg")Таким образом надо хардкодить get-метод для каждого типа данных, что не есть гуд как по мне.
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 (как вы в примере выше) - сам себе злой буратино. Да, можно даблы инициировать интами, мир неидеален, смиритесь.