Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Контейнер для гетерогенных данных 
:(
    Опции темы
Lazin
Дата 29.4.2009, 19:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



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

struct A
{
char category;
int index;
int64 timestamp;
double data;
};

struct B
{
char category;
int index;
int32 timedelta;
float data;
};


struct C
{
char category;
int index;
int64 timestamp;
uint32 data;
int32 time2;
};

и так далее
мне нужно с этими данными работать единообразно, и желательно, иметь возможность работать с контейнером как со стандартным, используя стандартные алгоритмы
к примеру мне может понадобиться найти элементы с максимальной меткой времени, но только имеющие тип A или C, и так далее...
Ваши советы? smile 
PM MAIL Skype GTalk   Вверх
azesmcar
Дата 29.4.2009, 19:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 81
Всего: 211



Lazin

У тебя определены эти классы - так? Что если их унаследовать от одного класса типа
Код

struct Base
{
   int index;
}

и убрать index из всех классов (я так понял это и есть идентификатор типа).
Хранить контейнер (к примеру лист) базовых обьектов, переписать алокатор чтобы не выделял память а брал обьект из буфера. 
Только вот при работе с ним придется cast делать. Но тут тоже можно обойтись. Если все index известны компайл-тайм можно использовать Typelist из Loki, но это только если все на компайл тайм писать.. я не знаю так получится или нет ибо не знаком со всей задачей.


Это сообщение отредактировал(а) azesmcar - 29.4.2009, 19:22
PM   Вверх
zim22
Дата 29.4.2009, 19:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


depict1
****


Профиль
Группа: Завсегдатай
Сообщений: 2682
Регистрация: 15.1.2009
Где: Украина

Репутация: 24
Всего: 69



А что, если определить свой класс-итератор, который будет пропускать невалидные типы?


--------------------
PM MAIL   Вверх
mes
Дата 29.4.2009, 20:06 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



имхо, слишком мало исходной информации, чтоб представить картину и пытаться найти подходящее решение ...


--------------------
PM MAIL WWW   Вверх
maxim1000
Дата 29.4.2009, 20:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 17
Всего: 110



можно сделать что-то std::vector<boost::any>
или более специализированное:
сделать базовый класс с чисто виртуальными функциями GetCategory, GetIndex
унаследовать от него шаблонный класс, который уже будет осуществлять доступ к конкретным структурам, сделать фабрику и натравить весь этот зоопарк на входной буфер - он его распарсит и даст доступ, спускаться к конкретному типу можно будет по dynamic_cast


--------------------
qqq
PM WWW   Вверх
Lazin
Дата 29.4.2009, 22:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Вот пример
Код

#include <iostream>
#include <cstdlib>
#include <ctime>

struct A
{
    char        id;
    int         index;
    float       value;
    long long   time;
};


struct B
{
    char        id;
    int         index;
    char        value[64];
    unsigned    time;
};

struct C
{
    char        id;
    int         index;
    double      value;
    unsigned    time;
};

char* GetData(size_t* size)
{
    size_t sz[] = {sizeof(A), sizeof(B), sizeof(C)};
    char* data = (char*)malloc(0x1000);
    char* p = data;
    int type = rand() % 3;
    int index = 0;
    while( true )
    {
        type = rand() % 3;
        index++;
        size_t available = (data + 0x1000) - p;
        if (available < sz[type])
        {
            *size = p - data;
            return data;
        }
        switch (type)
        {
            case 0:
            {
                A* a = (A*)p;
                a->id = type;
                a->index = index;
                a->value = (rand() % 100) / 10.0f;
                p += sizeof(A);
                break;
            }
            case 1:
            {
                B* b = (B*)p;
                b->id = type;
                b->index = index;
                p += sizeof(B);
                break;
            }
            case 3:
            {
                C* c = (C*)p;
                c->id = type;
                c->index = index;
                p += sizeof(C);
                break;
            }
        };
    }
}

A* find_first_A_gt(char* buffer, size_t size, float value)
{
    size_t sz[] = {sizeof(A), sizeof(B), sizeof(C)};
    char* p = buffer;
    while(p - buffer < size)
    {
        char id = *p;
        //filter
        if (id == 0)
        {
            A* a = (A*)p;
            if (a->value > value)
                //done
                return a;
        }
        p += sz[id];
    }
    return 0;
}

int main(int argc, char**argv)
{
    srand(time(0));
    size_t size = 0;
    char* buffer = GetData(&size);

    //decode
    A* a = find_first_A_gt(buffer, size, 9.0f);
    if (a)
        std::cout << a->index << std::endl;
    else
        std::cout << "fail" << std::endl;
    return 0;
}


сами структуры не важны, это просто упрощенный пример
итак, буфер содержит данные разных категорий - A, B и C
данные разных категорий содержат как общие поля (id, index), так и специфические для данной категории(value, time)
мне нужно по разному обрабатывать разные подмножества этих данных, к примеру найти среди данных категории А, первое, у которого value больше заданого значения, либо найти среди данных всех категорий те, у которых метка времени меньше заданой (метки времени данных разных категорий нужно привести к одному типу)
все это хардкодить не хочется

Добавлено через 5 минут и 55 секунд
Цитата(azesmcar @  29.4.2009,  19:18 Найти цитируемый пост)
У тебя определены эти классы - так? Что если их унаследовать от одного класса типа

это детали реализации, суть не в этом

Цитата(azesmcar @  29.4.2009,  19:18 Найти цитируемый пост)
Хранить контейнер (к примеру лист) базовых обьектов

нет, данные должны храниться так, как они пришли, потому-что их там очень много, их нужно интерпритировать по мере необходимости smile 
Цитата(zim22 @  29.4.2009,  19:54 Найти цитируемый пост)
А что, если определить свой класс-итератор, который будет пропускать невалидные типы? 

тепло smile 
Цитата(maxim1000 @  29.4.2009,  20:06 Найти цитируемый пост)
можно сделать что-то std::vector<boost::any>

нельзя, слишком ресурсоемко
Цитата(maxim1000 @  29.4.2009,  20:06 Найти цитируемый пост)
сделать базовый класс с чисто виртуальными функциями GetCategory, GetIndex
унаследовать от него шаблонный класс, который уже будет осуществлять доступ к конкретным структурам, сделать фабрику и натравить весь этот зоопарк на входной буфер - он его распарсит и даст доступ, спускаться к конкретному типу можно будет по dynamic_cast 

то-же самое, данные не должны меняться, их нужно просто фильтровать, но только условие фильтрации не должно быть задано жестко

Добавлено через 11 минут и 14 секунд
пока что я думаю сделать как нибудь так
Код


char* data = GetData(&size);

immutable_buffer buffer(data, size);//буфер с данными

immutable_buffer:: template view< ...параметры фильтрации буфера... > my_view( buffer );
//далее, можно работать с my_view, как с обычным контейнером, только неизменяемым

std::find_if( my_view.begin(), my_view.end(), my_predicate(7) );
...

единственное что я пока не понимаю, это как задавать параметры фильтрации буфера...
PM MAIL Skype GTalk   Вверх
sdukshis
Дата 29.4.2009, 23:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 92
Регистрация: 23.3.2009

Репутация: нет
Всего: 1



Использовать паттерн адаптер (иногда называют обёркта).
Суть примерно следующая:
Создаётся новая иерархия классов с одним базовым классом и потомками (по одному потомку на обрабатываемый тип)
В базовом классе с использованием виртуальных функций задается интерфейс для необходимого функционала (то что Вам необходимо)

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

Хранить адаптеры можно путём приведения к указателю на базовый класс и используя, например list<BaseAdapter*>

Создавать с использованием фабричного метод (фактические та же инструкция switch только надо возвращать return new ...)

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

P.S. Если интересует литература: Гамма, Хелм, Джонсон, Влиссидес - "Приёмы ОО проектирования. Паттерны проектирования"
PM MAIL   Вверх
mes
Дата 29.4.2009, 23:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



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

#include <iostream>
#include <cstdlib>
#include <ctime>
struct A
{
    char        id;
    int         index;
    float       value;
    long long   time;
};
struct B
{
    char        id;
    int         index;
    char        value[64];
    unsigned    time;
};
struct C
{
    char        id;
    int         index;
    double      value;
    unsigned    time;
};
char* GetData(size_t* size)
{
    size_t sz[] = {sizeof(A), sizeof(B), sizeof(C)};
    char* data = (char*)malloc(0x1000);
    char* p = data;
    int type = rand() % 3;
    int index = 0;
    while( true )
    {
        type = rand() % 3;
        index++;
        size_t available = (data + 0x1000) - p;
        if (available < sz[type])
        {
            *size = p - data;
            return data;
        }
        switch (type)
        {
            case 0:
            {
                A* a = (A*)p;
                a->id = type;
                a->index = index;
                a->value = (rand() % 100) / 10.0f;
                p += sizeof(A);
                break;
            }
            case 1:
            {
                B* b = (B*)p;
                b->id = type;
                b->index = index;
                p += sizeof(B);
                break;
            }
            case 3:
            {
                C* c = (C*)p;
                c->id = type;
                c->index = index;
                p += sizeof(C);
                break;
            }
        };
    }
}

struct A_IOp { virtual ~A_IOp() {}; virtual void operator () (A*) =0; };
struct B_IOp { virtual ~B_IOp() {}; virtual void operator () (B*) =0; };
struct C_IOp { virtual ~C_IOp() {}; virtual void operator () (C*) =0; };

struct ABC_Filter
{
    A_IOp * OnA;
    B_IOp * OnB;
    C_IOp * OnC;
};


struct A_Print : A_IOp
{

    virtual void operator () (A* a) { std::cout <<" " << a->value; }
};

struct A_Condition { virtual ~A_Condition() {}; virtual bool operator () (A*) =0; };

struct A_Print_with_Contition : A_IOp
{
    A_Condition * condition;
    virtual void operator () (A* a) { if (condition) if ((*condition)(a)) std::cout <<" " << a->value; }
};

void ABC_foreach (char * const buffer, const int size, ABC_Filter*  filter)
{
      if (!buffer || !filter) return;
      char * p=buffer;
      while(p - buffer < size)
     {
        char id = *p;
        //filter
        switch (id)
        {
            case 0 : if (filter->OnA) (*filter->OnA)((A*)p); p += sizeof(A); break;
            case 1 : if (filter->OnB) (*filter->OnB)((B*)p); p += sizeof(B); break;
            case 2 : if (filter->OnC) (*filter->OnC)((C*)p); p += sizeof(C); break;
        }
    }
}

int main(int argc, char**argv)
{
    srand(time(0));
    size_t size = 0;
    char* buffer = GetData(&size);
//
   ABC_Filter filter;
   A_Print A_print;
   filter.OnA =&A_print;
   filter.OnB=NULL;
   filter.OnC=NULL;

   ABC_foreach (buffer, size, &filter);

   std::cout << std::endl;
   std::cout << std::endl;

   struct : A_Condition { virtual bool operator () (A* a) { return (a->value>1 && a->value<5) ? true : false; } } a_my_cond;
   A_Print_with_Contition A_cprint;
   A_cprint.condition = &a_my_cond;

   filter.OnA =&A_cprint;

   ABC_foreach (buffer, size, &filter);

   system ("pause");

    return 0;
}





--------------------
PM MAIL WWW   Вверх
Lazin
Дата 30.4.2009, 08:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(sdukshis @  29.4.2009,  23:14 Найти цитируемый пост)
Использовать паттерн адаптер (иногда называют обёркта).
Суть примерно следующая:
Создаётся новая иерархия классов с одним базовым классом и потомками (по одному потомку на обрабатываемый тип)
В базовом классе с использованием виртуальных функций задается интерфейс для необходимого функционала (то что Вам необходимо)

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

Хранить адаптеры можно путём приведения к указателю на базовый класс и используя, например list<BaseAdapter*>

Создавать с использованием фабричного метод (фактические та же инструкция switch только надо возвращать return new ...)

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

P.S. Если интересует литература: Гамма, Хелм, Джонсон, Влиссидес - "Приёмы ОО проектирования. Паттерны проектирования" 

все очень понятно, но есть один нюанс 
Цитата(Lazin @  29.4.2009,  22:47 Найти цитируемый пост)
Цитата

Хранить контейнер (к примеру лист) базовых обьектов

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

представь, что их там миллионы, ты предлагаешь для каждого из них создать новый объект в куче и к тому-же повысить уровень косвенности при обращении(вызов виртуальной ф-ии, доступ к объекту через указатель на базовый класс)? no way smile

Добавлено через 4 минуты и 41 секунду
Цитата(mes @  29.4.2009,  23:43 Найти цитируемый пост)
поэксперементировал с одной идейкой, вот код, правда ужасно грязный, но сейчас нет времени его доводить до ума, но принцип я думаю виден

ага smile 
PM MAIL Skype GTalk   Вверх
maxim1000
Дата 30.4.2009, 08:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 17
Всего: 110



Цитата(Lazin @  29.4.2009,  22:47 Найти цитируемый пост)
нельзя, слишком ресурсоемко

ну тогда можно сам вектор не создавать, а просто сделать интерфейс, как у него, точнее, даже подмножество интерфейса:
1. инициализировать
2. получить указатель на текущий объект
3. перейти к следующему
4. проверить на окончание



--------------------
qqq
PM WWW   Вверх
azesmcar
Дата 30.4.2009, 08:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


uploading...
****


Профиль
Группа: Участник Клуба
Сообщений: 6291
Регистрация: 12.11.2004
Где: Армения

Репутация: 81
Всего: 211



Lazin

ну тогда остается как вижу один вариант, написать свой, стл совместимый контейнер, со своими итераторами (думаю тут подойдет forward iterator), передавать в конструкторе буфер и количество элементов в нем. а дальше как с обычным
Код

for (mycontainer::iterator it = c.begin(); it != c.end(); ++it)
{
   switch ( it->index )
   {
       case 1: it->ConvertToObject<Type1>(); ...; break;
       case 2: it->ConvertToObject<Type2>(); ...; break;
       case 3: it->ConvertToObject<Type3>(); ...; break;
       default: throw std::runtime_error("unknown object");
   }

}

чтобы не писать этот код везде написать предикаты. Еще как вариант. Можно switch вообще убрать, но тогда придется работать с базовым типом (если он есть)
PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0614 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.