Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Ноябрь 01, 2014, 15:35



Название: Пустая структура
Отправлено: Igors от Ноябрь 01, 2014, 15:35
Добрый день

Увидел такой приемчик

Код
C++ (Qt)
 
static struct StepTy {
} step MAYBE_UNUSED;
 
static struct EmptyTy {
} empty MAYBE_UNUSED;
 

Макруха для понту, можно не обращать внимания
Код
C++ (Qt)
 
#ifdef __GNUC__
 #define MAYBE_UNUSED __attribute__((used))
#else
 #define MAYBE_UNUSED
#endif
 

И оказывается, "пустые" структуры очень лихо юзаются  :)


Название: Re: Пустая структура
Отправлено: _Bers от Ноябрь 01, 2014, 19:00
Используются. Ну и ?


Название: Re: Пустая структура
Отправлено: Igors от Ноябрь 01, 2014, 19:03
Используются. Ну и ?
То Вы такой продвинутый, а для меня это было маленьким открытием. Типа "во, блин, и так можно" :)


Название: Re: Пустая структура
Отправлено: _Bers от Ноябрь 01, 2014, 19:10
а для меня это было маленьким открытием. Типа "во, блин, и так можно"

Сказать "вау!"  - это единственная причина создания этой темы?


Название: Re: Пустая структура
Отправлено: Bepec от Ноябрь 01, 2014, 20:19
Бредовая тема, если честно.


Название: Re: Пустая структура
Отправлено: gil9red от Ноябрь 01, 2014, 21:43
Объясните, пожалуйста, а для чего нужны пустые структуры?


Название: Re: Пустая структура
Отправлено: Igors от Ноябрь 02, 2014, 11:54
Объясните, пожалуйста, а для чего нужны пустые структуры?
Пример использования

Код
C++ (Qt)
struct BBox {
BBox( void ) {}
BBox( EmptyTy ) : lower(inf), upper(-inf) {}
..
};
..
BBox box(empty);


Название: Re: Пустая структура
Отправлено: gil9red от Ноябрь 02, 2014, 17:15
Объясните, пожалуйста, а для чего нужны пустые структуры?
Пример использования

Код
C++ (Qt)
struct BBox {
BBox( void ) {}
BBox( EmptyTy ) : lower(inf), upper(-inf) {}
..
};
..
BBox box(empty);

Структура не пустая: есть конструкторы и судя по второму конструктору, присутствуют поля lower и upper.

В первом сообщение показаны 2 структуры, и мне не понятно зачем они вообще нужны? Какую роль выполняют :)


Название: Re: Пустая структура
Отправлено: Igors от Ноябрь 02, 2014, 19:59
Структура не пустая: есть конструкторы и судя по второму конструктору, присутствуют поля lower и upper.

В первом сообщение показаны 2 структуры, и мне не понятно зачем они вообще нужны? Какую роль выполняют :)
Второй конструктор BBox принимает аргумент EmptyTy (пустая структура из первого сообщения)


Название: Re: Пустая структура
Отправлено: Bepec от Ноябрь 02, 2014, 20:49
В общем как то используются, а как он объяснить не может :)
Приведите полный код структуры использующей пустую структуру :)


Название: Re: Пустая структура
Отправлено: Johnik от Ноябрь 02, 2014, 20:53
Пример использования: O Boost Multi-index Containers (http://habrahabr.ru/post/160009/#habracut)


Название: Re: Пустая структура
Отправлено: gil9red от Ноябрь 02, 2014, 22:09
Пример использования: O Boost Multi-index Containers (http://habrahabr.ru/post/160009/#habracut)

Вижу :)


Название: Re: Пустая структура
Отправлено: _Bers от Ноябрь 03, 2014, 00:04
Пример использования: O Boost Multi-index Containers (http://habrahabr.ru/post/160009/#habracut)

Вы ошибаетесь. Пример ввел вас в заблуждение.

Если вы ещё раз внимательно посмотрите на этот пример, то вы заметите: нигде не используются объекты типов-тэгов.

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


Вот настоящий пример использования пустышек:


В рамках с++03 реализация имитации variardic template (функций с переменным количеством аргументов, с сохранением типо безопасности):

Код:
void Foo(Arg a1, Arg a2 = SEmpty(), Arg a3 = SEmpty() )
{
    ...
}

Здесь можно указать от 1 до 3х аргументов.

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

При вызове функции Foo, все не указанные аргументы по умолчанию превратятся в пустышку SEmpty.

Внутри туловища функции Foo ожидают, что часть аргументов может быть пустышками.
Там умеют корректно обработать "отсутствие аргумента".

В те времена, когда ещё не было variardic template, данная практика позволяла написать всего одну не шаблонную функцию,  вместо перегрузки множества шаблоно-функций.

И поскольку, функция не шаблонная, её можно сделать виртуальной.

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


Название: Re: Пустая структура
Отправлено: Igors от Ноябрь 03, 2014, 08:17
Вы ошибаетесь. Пример ввел вас в заблуждение.
Это open-sources которые я сейчас прикручиваю. Не вижу где я ошибаюсь :) Пример из буста намного сложнее, но смысл тот же: пустышка "натравливает" на вызов именно этой ф-ции/метода, где знают что делать. 

В рамках с++03 реализация имитации variardic template (функций с переменным количеством аргументов, с сохранением типо безопасности):

Код:
void Foo(Arg a1, Arg a2 = SEmpty(), Arg a3 = SEmpty() )
{
    ...
}

Здесь можно указать от 1 до 3х аргументов.

Arg - это механизм, способный принять ссылку любого объекта и проконтролировать типо безопасность.
(при извлечении будет выполнена проверка квалификаторов и типов)
Под извлечением имелась ввиду "инстанциация"? Но ведь ф-ция не template. Непонятно, объясните.

Спвсибо


Название: Re: Пустая структура
Отправлено: sergek от Ноябрь 03, 2014, 10:51
По-моему, самое очевидный пример использования таких структур/классов - в обработке исключений.


Название: Re: Пустая структура
Отправлено: _Bers от Ноябрь 03, 2014, 14:06
Не вижу где я ошибаюсь

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

А в этом случае, вам достаточно лишь forward declaration.
А вот определение типа - избыточно.

Пример:

http://rextester.com/RQSPH39005



Код:
#include <iostream>

struct Example
{
    template<class T> Example(const T& src)   //<--- T может быть либо int, либо float
    {
        enum { eIS_INT   = ::std::is_same<T, int>::value   };
        enum { eIS_FLOAT = ::std::is_same<T, float>::value };
        enum { eIS_VALID = eIS_FLOAT || eIS_INT };
        
        static_assert(eIS_VALID, "SORRY, BUT TYPE MUST BE INT OR FLOAT. OTHER TYPES NOT SUPPORT");
        
        typedef typename ::std::conditional<eIS_INT,   //<--- В зависимости от T выбирается стратегия его обработки
                    StrategyInt, StrategyFloat          
                >::type
            Strategy;
        
        Process( (Strategy*)nullptr );  //<--- типы-тэги нужны лишь для ручного управления выбором
                                        //      перегруженной функции-члена Process
                                        //    сами же объекты типов-тэгов нам не нужны
        
    }
private:
    struct StrategyInt;    //<--- поскольку объекты типов-тэгов нам не нужны,
    struct StrategyFloat;  // то мы вообще не делаем туловища для этих типов

    
    void Process(const StrategyInt*)                   //<--- forward declaration вполне достаточно,
    {                                                  //что бы контролировать выбор перегруженных функций
        ::std::cout<<" selected strategy for int\n";   //и что бы контролировать процесс инстанцирования шаблонов,
    }                                                  //и для любых других задач, где тип используется для ветвлений алгоритмов
    void Process(const StrategyFloat*)                
    {
        ::std::cout<<" selected strategy for float\n";
    }
};

int main()
{
    std::cout << "Hello, world!\n";
    
    Example(10);
    Example(10.5f);
}

Пример конечно утрирован, сферический конь в ваккуме. Но бывают задачи, когда выбор стратегии дальнейших действий - штука не тривиальная. Где то в шаблонах в зависимости от свойств каких то типов выбирается нечто.

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

Ситуация, когда вам не нужны объекты пустышки, и вы можете полностью сохранить и идею использования и реализацию без использования объектов типов-тэгов, это примеры-иллюстрации использования forward-declaration, а вовсе не пример использования типов-пустышек.

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


Под извлечением имелась ввиду "инстанциация"? Но ведь ф-ция не template. Непонятно, объясните.
Спвсибо

Под "извлечением" подразумевалось "извлечение".

Здесь нужно немножко понимать, как работает механизм Arg.

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

Код:
virtual foo(  const T& source_data );

К сожалению, сделать это не возможно.

И тогда самое простое и сердитое решение: использовать универсальный тип данных:

Код:
virtual foo(  const void* source_data );

Это будет работать. Но это не надежно, потому что  теряется контроль за типами аргументов.

Вот если бы было тоже самое, но с сохранением проверок типов и квалификаторов...

И тогда на сцену выходит механизм Arg.
Он запоминает ссылки на аргументы любых типов, и выполняет проверки типов и квалификаторов.

Причем доступен режим, при котором в релизе все проверки будут удалены. И получится тот же самый void*.
А есть режимы, когда он будет постоянно кидаться ассертами, или исключениями.

Область применения Arg - аргументы функций в условиях, когда заранее не известны ни их типы, ни их количество.


Изначально, я разработал механизм Arg, что бы с его помощью построить динамический делегат:


Код:
sample obj;
Connector con( obj, &sample::foo ); //<--- нацеливание делегата на функцию-член

con(10,20); //<--- запуск. Эквивалентен: obj.foo(10,20)

В отличие от статических делегатов,
таких как TConnector или std::function, динамический Connetor не является продуктом шаблона.

Это обычный класс. Весьма простой в использовании.
Он умеет нацеливаться на любые функци, функции-члены, функторы времени выполнения.

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


Название: Re: Пустая структура
Отправлено: _Bers от Ноябрь 03, 2014, 14:07
По-моему, самое очевидный пример использования таких структур/классов - в обработке исключений.

Очевидный юзкейс исключений - наследоваться от std::exception.

Использование же пустышок для этих целей - не очевидный, и не профессиональный ход.