Как передать в функцию произвольный член объединения?
От: KernelMode  
Дата: 27.01.10 13:28
Оценка:
Есть такая структура VARIANT (находится в OAIdl.h) у нее среди прочего есть огромное объединение, в котором сидит куча самых разнах форматов. Программа использует много функций, которые возвращают указатель на SAFEARRAY из этих структур. Причем, разные фумкции используют в VARIANT разные форматы. Есть вот такая функция для вытаскивания значений одного конкретного формата.

void ReadSafeArray (const SAFEARRAY *src,
                    std::vector<DWORD> & dest)
{
    dest.clear ();
    if (src != NULL)
    {
        ULONG    items (0);

        for (ULONG count = 0; count < src->cDims; count++)
        {
            items += src->rgsabound[count].cElements;
        }
        for (ULONG item = 0; item < items; item++)
        {
            dest.push_back ((((VARIANT *) src->pvData) + item)->lVal);
        }
    }
}


Поскольку фоматов множество, то хочется параметризировать функцию типом значения и считываемым членом объединения. И вот здесь я встал в тупик. Тип вектора, понятное дело; задается без проблем херез std::vector<T> А как задать считываемый член объединения? Т.е. как указать, что в выражении
(((VARIANT *) src->pvData) + item)->lVal
в одном случае надо брать lVal, в другом date, в третьем fltVal и т.д.? Пробовал через связку std::for_each и boost::bind. Работает, но выглядит очень запутанным. Может есть более простое решение?
Re: Как передать в функцию произвольный член объединения?
От: zaufi Земля  
Дата: 27.01.10 14:02
Оценка: 3 (1) +1
Здравствуйте, KernelMode, Вы писали:

KM>Есть такая структура VARIANT (находится в OAIdl.h) у нее среди прочего есть огромное объединение, в котором сидит куча самых разнах форматов. Программа использует много функций, которые возвращают указатель на SAFEARRAY из этих структур. Причем, разные фумкции используют в VARIANT разные форматы. Есть вот такая функция для вытаскивания значений одного конкретного формата.


<skip>

KM>Поскольку фоматов множество, то хочется параметризировать функцию типом значения и считываемым членом объединения. И вот здесь я встал в тупик. Тип вектора, понятное дело; задается без проблем херез std::vector<T> А как задать считываемый член объединения? Т.е. как указать, что в выражении
(((VARIANT *) src->pvData) + item)->lVal
в одном случае надо брать lVal, в другом date, в третьем fltVal и т.д.? Пробовал через связку std::for_each и boost::bind. Работает, но выглядит очень запутанным. Может есть более простое решение?


ну во первых я бы не стал передавать сюда контейнер... imho, более удобный способ передвать output iterator -- таким образом ты не навязываешь вызывающему тип контейнера (включая параметры которыми он инстанциирован... ну кто вот сказал что дефолтный аллокатор это хорошо во всех случаях)

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


template <typename UnionMemberType, typename OutputIterator>
void ReadSafeArray (
    const SAFEARRAY* src
  , UnionMemberType VARIANT::* field
  , OutputIterator it
  )
{
    if (src != NULL)
    {
        ULONG items(0);

        // напрашивается std::count
        for (ULONG count = 0; count < src->cDims; count++)
            items += src->rgsabound[count].cElements;

        for (ULONG item = 0; item < items; item++)
            *it++ = ((((VARIANT *) src->pvData) + item)->*field)
    }
}


также можно ассертнуть что value_type твоего итератора тотже что и UnionMemberType. (ну или последний приводится к первому)

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

template <typename OutputIterator>
void ReadSafeArray (
    const SAFEARRAY* src
  , OutputIterator it
  )
{
    ...
}


т.е. тип который необходимо достать из обединения мы узнаем по value_type итератора...
Re: Как передать в функцию произвольный член объединения?
От: _nn_ www.nemerleweb.com
Дата: 27.01.10 14:12
Оценка:
Здравствуйте, KernelMode, Вы писали:

ATL::CComSafeArray может облегчить работу.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Как передать в функцию произвольный член объединения?
От: KernelMode  
Дата: 27.01.10 14:28
Оценка:
Здравствуйте, zaufi, Вы писали:

Z>Здравствуйте, KernelMode, Вы писали:


Z>чтобы решить проблему с выбором конкретного типа из объединения можно во-первыз передавать смещение до нужного поля как параметр (в т.ч. и шаблонный) в функцию... например так:


Все заработало. Огромное спасибо!
Re[2]: Как передать в функцию произвольный член объединения?
От: KernelMode  
Дата: 27.01.10 14:32
Оценка:
Здравствуйте, _nn_, Вы писали:

__>ATL::CComSafeArray может облегчить работу.


Он поддерживает далеко не все форматы, которые есть в VARIANT.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.