Пушыстый (_winnie) wrote,
Пушыстый
_winnie

G++ lambda strikes back.

SORT(vec, return _1->pos.y < _2->pos.y; );
_Winnie C++ Colorizer



Вкусные расширения gcc.

1) __typeof__.
Выражения, вроде __typeof__(my_list)::iterator не осиливает (предполагаю, просто не предусмотрели в грамматике), поэтому

template <class T> struct typeof_helper { typedef T type; };
#define TYPEOF(x) typeof_helper<__typeof__(x)>::type
_Winnie C++ Colorizer


и дальше можно использовать как TYPEOF(x)::iterator.

Это позвляет не задавать явно типы во многих случаях.
Например,
#define MY_FOR_EACH(i, c) for (TYPEOF(container)::iterator i == c.begin;  i != c.end(); ++i)
или 
#define AUTO(var, initializer)  TYPEOF(initializer) var(initializer);
или 
TYPEOF(A() + B()) operator+(A, B)
и тп.
_Winnie C++ Colorizer


Есть небольшая проблема - если мы в шаблонной функции, и x зависит от параметра шаблона, то надо ставить перед TYPEOF ключевое слово typename, и если мы используем TYPEOF в макросе, то нельзя понять надо ставить typename или нет. Не знаю как это обойти без дублицирования макросов дважды. Возможно, эту конструкцию в макросах лучше вообще не использовать, а писать без неё в два приёма - typedef __typeof(x)__ type; typedef type::iterator iterator;

2) операторы внутри выражения ({ ... }). Пример из справки gcc:

     #define maxint(a,b) \
       ({int _a = (a), _b = (b); _a > _b ? _a : _b; }) 
_Winnie C++ Colorizer


Значением выражения является значение последней инструкции в их спике. Например, значение ({int a = 10; a + 3;}) - 13. Временные переменные внутри макро min позволяют избежать двойного использования аргумента макроса в выражении вроде min(foo(), nya(bar())) где foo/nya/bar - какие-то сложные функции.

Теперь, о том, какие вкусности из этого можно сделать. Дальше - не один кусок кода, который демонстрирует что-то только одно, а сразу всё вперемежку, поэтому не надо боятся его размера, там одно на другое не запутано :)

#define FUNCTION(RET, PARAM_AND_BODY) ({struct foo__ { static RET f__ PARAM_AND_BODY };&(foo__::f__);})

//------------
//typeof
template <class T> struct typeof_helper { typedef T type; };
#define TYPEOF(x) typeof_helper<__typeof__(x)>::type


//------------
#define FOR_EACH(CONTAINER, CODE) \
do \
{ \
    TYPEOF(CONTAINER) &ref__(CONTAINER); /* избегать множественного использования CONTAINER в макросе */ \
    for (TYPEOF(CONTAINER)::iterator iter__ = ref__.begin(), end__ = ref__.end(); iter__ != end__; ++iter__) \
    {\
        if (bool fake_bool__ = true) for (TYPEOF(CONTAINER)::value_type &_ = *iter__; fake_bool__; fake_bool__ = false) \
        { \
            CODE; \
        } \
    } \
} \
while (0) 

//------------
#define SORT(CONTAINER, CODE) \
do \
{ \
    TYPEOF(CONTAINER) &ref__(CONTAINER); /* избегать множественного использования CONTAINER в макросе */ \
    typedef TYPEOF(CONTAINER)::value_type param_t__; \
    std::sort(ref__.begin(), ref__.end(), FUNCTION(bool, (const param_t__ &_1, const param_t__ &_2) { CODE }) ); \
} \
while (0) 


//------------
#include <vector>
#include <iostream>
#include <boost/bind.hpp> //нужен только для пример биндинга, больше ни для чего.


struct Vec3D { float x, y, z; };

struct Entity
{
    Vec3D pos;
    float health;
    void Kill() {}
    void Update() {}
};

int main()
{
    std::cout << FUNCTION(int, (int a, int b) { return a + b; })(10, 20);
    std::cout << FUNCTION(int, (int a, int b) { return a - b; })(10, 20);

    {

        std::vector<Entity *> vec;
        vec.push_back(new Entity);
        vec.push_back(new Entity);

        vec[0]->;pos.y = 30;
        vec[1]->pos.y = 20;

        FOR_EACH(vec,
            std::cout << _->pos.x <<' ' <<_->pos.y << ' '<< _->pos.z; 
        printf(" - %f %f %f\n", _->pos.x, _->pos.y, _->pos.z);
        );

        SORT(vec, return _1->pos.y < _2->pos.y; );

        std::for_each(vec.begin(), vec.end(), FUNCTION(void, (Entity *ptr) { std::cout << ptr->pos.y; }) );
    }

    {

        std::vector<Entity *> vec;


        std::for_each(vec.begin(), vec.end(),
        boost::bind(
            FUNCTION(int, (float health, Entity *ent) { 
                if (ent->health < health) ent->Kill(); else ent->Update(); 
            }),
            0.7,//heath
            _1));
    }
}
_Winnie C++ Colorizer


Для макросов, которые не меняют последовательность (FIND, FOR_EACH), нужно написать версии, учитывающие возможную const-квалификацию контейнеров (const_iterator <-> iterator, T& <-> const T&), но это бы немного загромоздило код, не показывая ничего особо интересного.



Странно, что об этой (нестандартной) особенности gcc, существующей уже хрен знает сколько, молчат в статьях про C++ и функторы. А она может очень помочь писать код. В отличие от косметических правок из "популистcких" статей Страуструпа про C++0x. Вот бы это расширение в стандарт. Вон, уже даже _готовый_ промышленный proof of concept готов - g++


У конструкции struct { static void foo() { ... } } как всегда, проблемы с чтением локальных переменных. Которые приходится подравнивать напильником boost::bind -
{
 int x, y, z; //локальные переменные 
 bind(FUNCTION(void, (int x, int y, int z) { ... }), x, y, z);
}
_Winnie C++ Colorizer

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


updated: proposal для стандартной лямбды в C++ by Bjarne:
http://www.research.att.com/~bs/N1968-lambda-expressions.pdf
Subscribe
  • Post a new comment

    Error

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 6 comments