вопрос проектирования
От: Oleg A. Bachin Украина  
Дата: 21.01.04 10:19
Оценка:
Здравствуйте знатоки!

я только начинаю изучать С++, поэтому позникают вопросы правильности применения тех или иных методов...

задача такая:
есть несколько типов данный. под типом (в данном контексте) подразумеваю родной тип С++ (или stl) плюс тип форматирования (выравнивать влево/вправо, значащие нули/нет и т.д..).

также у каждого элемента кроме типа есть размер, т.е. его фиксированная длинна в буффере. (я еще не выбрал как удобнее будет работать через буффер или поток. поток вроде как логичнее, но могут быть проблеммы с итераторами. только об этом потом...)

написал примерно следующее:


namespace item_type
{

template <typename T>
class base_item_type
{
        public:
                typedef T::value_type value_type;

                base_item_type(int length)
                {
                        
                };

                void write() {};
                void read() {};
        private:
                value_type m_value;
};

namespace format_style
{
        class C
        {
                public:
                        typedef std::string value_type;
        };
}

typedef base_item_type<format_style::C> C;

}


теперь хотелось бы ф-цию форматирования (а возможно и просто копирования в буффер) вынести в сообветствующих класс форматирования. для этого можно реализовать соответствующую ф-цию:


class C
{
        public:
                typedef std::string value_type;
                static void write(const std::ostream &os, 
                  const value_type &value, int length) {};
};


и вызывать соответственно:

base_item_type::write(){T::write(...)}


только что-то мне это не нравится...
не то static не нравится, не то еще что-то... вроде как предчувствия в проектировании...
да и вообще хотелось бы inline...

ладно, перейдем ко второй части...

теперь хотелось бы поиметь сооветствующий класс со списком таких элементов. (именованным списком).

что-то типа:

class file_001
{
  item_type::C item1(10);
  item_type::D item2(2);
...

}


вот тут то я и передумал насчет потоков...
захотелось (посмотрев auto_value из "Исходников") сделать автоматическое создание, плюс вычисление сдвига относительно начала буффера...
тогда двигаясь по списку (класс file_001 это элемент списка) мы можем легко читать/писать любой элемент (лучше даже назвать поле item1, item2 ...)

прийдя к такому выводу решил что можно конвертировать буффер на лету и позвращая
const value при чтении ну и перекрыть оператор =.

таким образом
value_type m_value;

не совсем уместно...

что думают знатоки по этому поводу? не сильно ли я навернул? может есть подобные примеры на посмотреть?

PS время не поджимает, так что пока экспериментировать могу вволю...

PSS были еще варианты с виртуальными методами read/write, но мне не понравилось...
такое я и на Delphi с пол-пинка напишу... поскольку учусь — хотелось бы на template. но если Вы считаете что так разумней — хотелось бы это услышать.

PSSS для самих классов собираюсь написать codegen... просто сами структуры уже есть в xml. под Delphi писал с генератором и виртуальными методами...
Best regards,
Oleg A. Bachin
Re: вопрос проектирования
От: Кодт Россия  
Дата: 21.01.04 11:17
Оценка:
Здравствуйте, Oleg A. Bachin, Вы писали:

OAB>я только начинаю изучать С++, поэтому позникают вопросы правильности применения тех или иных методов...


OAB>задача такая:

OAB>есть несколько типов данный. под типом (в данном контексте) подразумеваю родной тип С++ (или stl) плюс тип форматирования (выравнивать влево/вправо, значащие нули/нет и т.д..).

OAB>также у каждого элемента кроме типа есть размер, т.е. его фиксированная длинна в буффере. (я еще не выбрал как удобнее будет работать через буффер или поток. поток вроде как логичнее, но могут быть проблеммы с итераторами. только об этом потом...)


Через поток — проще. Есть strstream, который выводит в строку.

Мне решение видится так:
template<class Data, class Fmt>
struct apply_fmt
{
  Data data; // пока что обойдемся без операторов приведения типа, как в auto_value.
};

template<class Data, class Fmt>
ostream& operator<< (ostream& ost, apply_fmt<Data,Fmt> const& v)
{
  return ostream << Fmt() << v.data;
}

Fmt — классы, экземпляры которых выводятся как команды для ostream.
Поскольку команды известны на стадии проектирования, то все детали запихаем
// отдельные команды

template<int w> struct fmt_setw {};

template<int w> ostream& operator<< (ostream& ost, fmt_setw<w> _)
{
  return ost << setw(w);
}

template<char f> struct fmt_setfill {};

template<char f> ostream& operator<< (ostream& ost, fmt_setfill<f> _)
{
  return ost << setfill(f);
}

template<int b> struct fmt_setbase {};

template<int b> ostream& operator<< (ostream& ost, fmt_setbase<b> _)
{
  return ost << setbase(b);
}

struct fmt_hex {};
inline ostream& operator<< (ostream& ost, fmt_hex _) { return ost << hex; }

struct fmt_dec {};
struct fmt_left {};
struct fmt_right {};
// и т.д.

// наконец, пустой формат
struct fmt_void {};

inline ostream& operator<< (ostream& ost, fmt_void _) { return ost; }

// связывание форматов
// можно воспользоваться Loki::TypeList или boost::что-то-там-щас-не-помню

template<class F1, class F2> struct fmt_chain {};

template<class F1, class F2> ostream& operator<< (ostream& ost, fmt_chain<F1,F2> _)
{
  return ost << F1() << F2();
}

#define FMT_CONS(f1,f2) fmt_pair< f1, f2 >
#define FMT_NULL fmt_void

#define FMT_LIST1(f1)          FMT_CONS(f1,FMT_NULL)
#define FMT_LIST2(f1,f2)       FMT_CONS(f1, FMT_LIST1(f2))
#define FMT_LIST3(f1,f2,f3)    FMT_CONS(f1, FMT_LIST2(f2,f3))
#define FMT_LIST4(f1,f2,f3,f4) FMT_CONS(f1, FMT_LIST3(f2,f3,f4))

Применение
typedef apply_fmt<unsigned long, FMT_LIST3(fmt_hex, fmt_setw<8>, fmt_setfill<'0'>)> hexulong;
hexulong n;
n.data = 0xdead;
cout << n;
Перекуём баги на фичи!
Re[2]: вопрос проектирования
От: Oleg A. Bachin Украина  
Дата: 21.01.04 13:02
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Через поток — проще. Есть strstream, который выводит в строку.

вероятно я не достаточно четко в конце описал почему через буффер удобнее (IMHO)...
и сразу (забегая вперед)... под именнованным списком понималась именно структура!
для чего?
чтобы можно было сделать так:


class A
{
  item_fmt<N> id(5);
  item_fmt<C> first_name(50);
}

my_vector<A> vecA;

std::cout << vecA.current.first_name;
vecA.current.id = 1234;





К>Мне решение видится так:

К>
К>template<class Data, class Fmt>
К>struct apply_fmt
К>{
К>  Data data; // пока что обойдемся без операторов приведения типа, как в auto_value.
К>};
эээ... эт конечно хорошо, но во второй части уклон был сделан именно на динамические чтение/запись в буффер.
т.е. непосредственное "value_type m_value" в классе не присутствует... 


К>template<class Data, class Fmt>
К>ostream& operator<< (ostream& ost, apply_fmt<Data,Fmt> const& v)
К>{
К>  return ostream << Fmt() << v.data;
К>}
К>

К>Fmt — классы, экземпляры которых выводятся как команды для ostream.
К>Поскольку команды известны на стадии проектирования, то все детали запихаем
К>[c]
К>// отдельные команды

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

К>template<int w> struct fmt_setw {};


К>template<int w> ostream& operator<< (ostream& ost, fmt_setw<w> _)

К>{
К> return ost << setw(w);
К>}

что есть setw?
чуть не по теме:
если есть принятые стандарты сокращений, то я их еще не знаю (практики нет)...
только что не мог понять почему не собирается строка кода типа memcpy(a, b, c). просто привык к порядку (source, destination, length)!

К>template<char f> struct fmt_setfill {};


К>template<char f> ostream& operator<< (ostream& ost, fmt_setfill<f> _)

К>{
К> return ost << setfill(f);
К>}

мне нужно 2 параметра: &value, length

К>// связывание форматов

К>// можно воспользоваться Loki::TypeList или boost::что-то-там-щас-не-помню

насчет связывания — я уже говорил: кодогенератор.... в начале написано почему...


всетаки это совсем не то что бы я хотел...

либо я паршиво описал что хочу (да я и сам вижу что паршиво ) либо вы не до конца вчитались...
но все равно спасибо за быстрый ответ. если есть еще какие идеи — всегда рад внимательно выслушать...
Best regards,
Oleg A. Bachin
Re[3]: вопрос проектирования
От: Кодт Россия  
Дата: 21.01.04 14:04
Оценка:
Здравствуйте, Oleg A. Bachin, Вы писали:

К>>Через поток — проще. Есть strstream, который выводит в строку.

OAB>вероятно я не достаточно четко в конце описал почему через буффер удобнее (IMHO)...
OAB>и сразу (забегая вперед)... под именнованным списком понималась именно структура!
OAB>для чего?
OAB>чтобы можно было сделать так:

OAB>class A
OAB>{
OAB>  item_fmt<N> id(5);
OAB>  item_fmt<C> first_name(50);
OAB>}

На С++ похоже. Но не С++.

<>

OAB>всетаки это совсем не то что бы я хотел...

OAB>либо я паршиво описал что хочу (да я и сам вижу что паршиво ) либо вы не до конца вчитались...

Я попробую угадать, что тебе надо.

Итак, есть набор структур, для каждой из которых хочется построить вывод в строку (и ввод из строки). Т.е. это задача сериализации.
Причем выходная строка должна быть читаемой, поэтому и делаем всякие танцы с выравниванием.

Для каждого элемента структуры, если это примитивный тип, хочется правила вывода поместить прямо в объявление.
Примерно так:
STRINGIZEABLE_MEMBER(int, ALIGN(LEFT), FILL('0'), RADIX(OCT)) x;

А для всей структуры нужно что-то вида
STRINGIZE_MEMBERS(x,y,z);

В общем, сделать метаданные своими руками.

Обобщенная задача решается на С++ довольно сложным путем.
Поэтому, если ты сможешь заузить и конкретизировать требования — это поможет продвинуться в решении задачи.

ЗЫ. Перед тем, как делать эскизы на С++ — попробуй выразить мысль на человеческом языке. И не старайся сразу углубляться в мелочи и детали. Devil in details, помнишь?
Перекуём баги на фичи!
Re[4]: вопрос проектирования
От: Oleg A. Bachin Украина  
Дата: 21.01.04 14:38
Оценка:
Здравствуйте, Кодт, Вы писали:
К>
OAB>>class A
OAB>>{
OAB>>  item_fmt<N> id(5);
OAB>>  item_fmt<C> first_name(50);
OAB>>}
К>

К>На С++ похоже. Но не С++.

Вот! я так и знал! именно это я хотел услышать!

К>Я попробую угадать, что тебе надо.

ок...

К>Итак, есть набор структур, для каждой из которых хочется построить вывод в строку (и ввод из строки). Т.е. это задача сериализации.

К>Причем выходная строка должна быть читаемой, поэтому и делаем всякие танцы с выравниванием.
точно!!! хотя и идея в другом — есть стандартизированный формат, придуман не мной. хочу сделать классы для удобных чтения/записи....

К>Для каждого элемента структуры, если это примитивный тип, хочется правила вывода поместить прямо в объявление.

К>Примерно так:
К>
STRINGIZEABLE_MEMBER(int, ALIGN(LEFT), FILL('0'), RADIX(OCT)) x;

здесь можно сузить...
т.е: есть порядка 15 типов... есть еще что-то типа перечисления, т.е.:
строка, которая может состоять только из '0', '1', '*', ' '
примерные типы я описывал. достаточно разобрать на строке — остальное я сам


К>А для всей структуры нужно что-то вида

К>
STRINGIZE_MEMBERS(x,y,z);


угу... только хотелось бы как я писал выше... хотя возможно я опять мыслю не теми понятиями...

К>В общем, сделать метаданные своими руками.

да.

К>Обобщенная задача решается на С++ довольно сложным путем.

мы легких путей не ищем! особенно сейчас!

К>Поэтому, если ты сможешь заузить и конкретизировать требования — это поможет продвинуться в решении задачи.

пробуем так:
все что сказали выше, тип только строка с выравниванием влево (С)...
структура из двух элементов длинами 1 и 2...

вектор и итераторы — тоже домашнее задание.

К>ЗЫ. Перед тем, как делать эскизы на С++ — попробуй выразить мысль на человеческом языке. И не старайся сразу углубляться в мелочи и детали. Devil in details, помнишь?

в том то все и дело, что незнание мелочей мне сейчас мешает мыслить... я не могу представить как это вообще обычно делается (не то чтобы следать конкретную задачу).
Best regards,
Oleg A. Bachin
Re[4]: вопрос проектирования
От: Oleg A. Bachin Украина  
Дата: 22.01.04 12:40
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Для каждого элемента структуры, если это примитивный тип, хочется правила вывода поместить прямо в объявление.

К>Примерно так:
К>
STRINGIZEABLE_MEMBER(int, ALIGN(LEFT), FILL('0'), RADIX(OCT)) x;


Ок. Что есть STRINGIZEABLE_MEMBER? Это macros или template?
Объясню почему мне это важно дальше...

К>А для всей структуры нужно что-то вида

К>
STRINGIZE_MEMBERS(x,y,z);

К>В общем, сделать метаданные своими руками.

Ну в общем наверное больше да чем нет...

К>Обобщенная задача решается на С++ довольно сложным путем.

К>Поэтому, если ты сможешь заузить и конкретизировать требования — это поможет продвинуться в решении задачи.

попробую конкретизировать:

у меня есть данные (входящие и исходящие). эти данные передаются в определенном формате.
в документации есть примерно следующее:
Типы данных:
"C" — строковый тип, выравнивание по левому краю, до длины добивается пробелами.
....
и т.д. все типы (как уже говорилось ~15 штук)

дальше

Структура файла 001:

Наименование какой-то фигни C[10]
Кол-во чего-то N[6]
.....

Таких структур порядка 50...

вот и хотелось бы иметь какое-то соответствие документация-код.

Все это собирается на лету. Я так понимаю, что Ваш пример подошел бы на тот случай, когда у меня уже есть какие-то бизнес объекты и мне захотелось из передать (сохранить состояние или что-то подобное).

1. почему хотелось бы иметь проверку формата до сериализации:
для проверки валидности данных. т.е. если у меня есть int, и ему при сериализации отведено 2 байта, то он не может быть больше 99. (ну и другие проверки нужны). в случае присвоения значения больше, я возбуждаю исключение. а дальше пусть внешний объект решает! строку может и обрежет, а может и нет... вобщем это уже бизнес-логика...

2. почему не хотелось бы иметь размерность, как параметр шаблона:
при варианте
template<int size, class format_style>
class item{}

item<1, C> i1;
item<2, C> i2;
item<3, C> i3;
item<4, C> i4;


компилятор создаст на 4 класса. (я ведь правильно понимаю?)
считаю это накладным...

как это все правильно делается?
Best regards,
Oleg A. Bachin
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.