[mini-trick] emulation of "anonymous types"
От: Evgeny.Panasyuk Россия  
Дата: 12.10.14 07:38
Оценка: 54 (3)
auto scope_value = 123;

cout << "NEW demo:" << endl;
auto xs = fusion::make_vector
(
    NEW((y, 0.5)(x, scope_value + 1)),
    NEW((x, 0.05)(y, 10)(p, 0.001))
);
fusion::for_each(xs, [](auto x)
{
    cout <<  "x.x = " << x.x << ", sizeof(x) = " << sizeof(x) << endl;
});

cout << endl << "NEW_LAZY demo:" << endl;
fusion::for_each(transform(xs, NEW_LAZY((y, scope_value + 11)(q, _1))), [](auto x)
{
    cout << "x.q.x = " << x.q.x << ", sizeof(x) = " << sizeof(x) << endl;
});
Output:
NEW demo:
x.x = 124, sizeof(x) = 16
x.x = 0.05, sizeof(x) = 24

NEW_LAZY demo:
x.q.x = 124, sizeof(x) = 24
x.q.x = 0.05, sizeof(x) = 32

LIVE DEMO
  code
// Copyright Evgeny Panasyuk 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// e-mail: E?????[dot]P???????[at]gmail.???

// Emulation of "Anonymous Types"

// GOTO main

#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/pop_front.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/fusion/container.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/phoenix/phoenix.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>
#include <iostream>
#include <utility>

/************************************************************************************************/
// refer http://www.boost.org/doc/libs/1_56_0/boost/fusion/adapted/struct/define_struct.hpp
// rerquired for sequence of tuples syntax ((a,b)(c,d)...)
#define ANONYMOUS_TYPE_0(X, Y) ((X, Y)) ANONYMOUS_TYPE_1
#define ANONYMOUS_TYPE_1(X, Y) ((X, Y)) ANONYMOUS_TYPE_0
#define ANONYMOUS_TYPE_0_END
#define ANONYMOUS_TYPE_1_END

#define VAR_NAME(elem) BOOST_PP_TUPLE_ELEM(2, 0, elem)
#define VAR_VALUE(elem) BOOST_PP_TUPLE_ELEM(2, 1, elem)
#define VALUES(r, data, i, elem) BOOST_PP_COMMA_IF(i) VAR_VALUE(elem)
#define DECLARE_MEMBER(r, data, i, elem) typename boost::mpl::at_c<Ts, i>::type VAR_NAME(elem);

#define FOR_EACH_DECL(action, seq) \
    BOOST_PP_SEQ_FOR_EACH_I(action, _, BOOST_PP_SEQ_POP_FRONT(BOOST_PP_CAT(ANONYMOUS_TYPE_0(0,0)seq,_END))) \
/**/

#define NEW_AUX(seq) \
    [](auto... args) \
    { \
        using Ts = boost::mpl::vector<decltype(args)...>; \
        struct \
        { \
            FOR_EACH_DECL(DECLARE_MEMBER, seq)\
        } anonymous = {std::move(args)...}; \
        return anonymous; \
    } \
/**/

#define NEW_LAZY(seq) (boost::phoenix::bind(NEW_AUX(seq), FOR_EACH_DECL(VALUES, seq)))
#define NEW(seq) (NEW_AUX(seq)(FOR_EACH_DECL(VALUES, seq)))
/************************************************************************************************/
int main()
{
    using namespace std;
    using namespace boost;
    using phoenix::arg_names::_1;

    auto scope_value = 123;
    
    cout << "NEW demo:" << endl;
    auto xs = fusion::make_vector
    (
        NEW((y, 0.5)(x, scope_value + 1)),
        NEW((x, 0.05)(y, 10)(p, 0.001))
    );
    fusion::for_each(xs, [](auto x)
    {
        cout <<  "x.x = " << x.x << ", sizeof(x) = " << sizeof(x) << endl;
    });
    
    cout << endl << "NEW_LAZY demo:" << endl;
    fusion::for_each(transform(xs, NEW_LAZY((y, scope_value + 11)(q, _1))), [](auto x)
    {
        cout << "x.q.x = " << x.q.x << ", sizeof(x) = " << sizeof(x) << endl;
    });
}

NEW_LAZY можно сделать на основе лямбд, без bind'а — тогда не будет bind expression, зато будет работать member access у аргументов.
Думаю при желании/необходимости можно адаптировать структуру к Boost.Fusion через CRTP (что-то наподобие BOOST_FUSION_DEFINE_STRUCT_INLINE).

Где применимо не знаю. Если такое потребуется — можно и лямбду вручную расписать.
Встретилось нечто подобное при решении одной задачки. Оставляю здесь — может кому пригодится / на какие мысли наведёт.

P.S. Эта реализация для C++14. В первом же варианте реализация была C++11, но без LAZY:
  first version
http://coliru.stacked-crooked.com/a/d38afecc3a2a893c
// Copyright Evgeny Panasyuk 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// e-mail: E?????[dot]P???????[at]gmail.???

// Emulation of "Anonymous Types"

// GOTO main

#include <boost/preprocessor/seq/pop_front.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/fusion/container.hpp>
#include <boost/fusion/algorithm.hpp>
#include <iostream>
#include <utility>

using namespace std;
using namespace boost;
using namespace fusion;

// refer http://www.boost.org/doc/libs/1_56_0/boost/fusion/adapted/struct/define_struct.hpp
#define ANONYMOUS_TYPE_0(X, Y) ((X, Y)) ANONYMOUS_TYPE_1
#define ANONYMOUS_TYPE_1(X, Y) ((X, Y)) ANONYMOUS_TYPE_0
#define ANONYMOUS_TYPE_0_END
#define ANONYMOUS_TYPE_1_END

#define VAR_NAME(elem) BOOST_PP_TUPLE_ELEM(2, 0, elem)
#define VAR_VALUE(elem) BOOST_PP_TUPLE_ELEM(2, 1, elem)
#define MOVE_VAR(r, data, elem) std::move(BOOST_PP_CAT(VAR_NAME(elem), __aux)),
#define DECLARE_VAR(r, data, elem) auto BOOST_PP_CAT(VAR_NAME(elem), __aux) = VAR_VALUE(elem);
#define DECLARE_MEMBER(r, data, elem) decltype(BOOST_PP_CAT(VAR_NAME(elem), __aux)) VAR_NAME(elem);

#define FOR_EACH_DECL(action, seq) \
    BOOST_PP_SEQ_FOR_EACH(action, _, BOOST_PP_SEQ_POP_FRONT(BOOST_PP_CAT(ANONYMOUS_TYPE_0(0,0)seq,_END))) \
/**/

#define NEW(seq) \
    [&] \
    { \
        FOR_EACH_DECL(DECLARE_VAR, seq)\
        struct \
        { \
            FOR_EACH_DECL(DECLARE_MEMBER, seq)\
        } anonymous = {FOR_EACH_DECL(MOVE_VAR, seq)}; \
        return anonymous; \
    }() \
/**/

int main()
{
    auto scope_value = 123;
    auto xs = make_vector
    (
        NEW((y, 0.5)(x, scope_value + 1)),
        NEW((x, 0.05)(y, 10)(p,0.001))
    );
    for_each(xs, [](auto x)
    {
        cout << "x.x = " << x.x << ", sizeof(x) = " << sizeof(x) << endl;
    });
}
Отредактировано 12.10.2014 9:00 Evgeny.Panasyuk . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.