Можно ли создать карту слияния из адаптированной структуры?

Пусть A be:

struct A {
  int a;
  std::string b;
  struct keys {
    struct a;
    struct b;
  };
};

Я хотел бы сгенерировать a fusion::map из структуры таким образом, чтобы он содержал fusion::pair s: fusion::pair<A::keys::a, int> и fusion::pair<A::keys::b, std::string>. Что-то вроде

A a;
fusion::make_map<A>(a)

Я пробовал с BOOST_FUSION_ADAPT_ASSOC_STRUCT

BOOST_FUSION_ADAPT_ASSOC_STRUCT(
    A,
    (int,  a, A::keys::a)
    (std::string, b, A::keys::b)

)

Это приспосабливает A, который будет использоваться как ассоциативная последовательность, но я не нашел способ построить из него карту. В частности, если я перебираю его, я получаю только значения. Если бы я мог перебирать ключи, которые были бы действительно полезны, потому что тогда я мог бы закрепить значения и ключи для построения карты, но я еще не нашел способ сделать это.

Ответ 1

Вы должны использовать Associative Iterator интерфейс - он обеспечивает result_of::key_of<I>::type metafunciton.

Я нашел код в своих старых записях и извлек соответствующую часть. Я использовал его во время реализации SoA vector (как я понимаю из чата - вы его тоже реализуете), но в итоге переключился на простое определение fusion::map. Я думаю, что сделал это, чтобы иметь унифицированный интерфейс нормальной структуры и структуры ссылок (т.е. Оба доступны через тег типа).

Live Demo on Coliru

namespace demo
{
    struct employee
    {
        std::string name;
        int age;
    };
}

namespace keys
{
    struct name;
    struct age;
}

BOOST_FUSION_ADAPT_ASSOC_STRUCT
(
    demo::employee,
    (std::string, name, keys::name)
    (int, age, keys::age)
)

template<typename> void type_is();

int main()
{
    type_is<as_fusion_map<demo::employee>::type>();
}

Результат:

main.cpp:(.text.startup+0x5): undefined reference to `void type_is<

boost::fusion::map
<
    boost::fusion::pair<keys::name, std::string>,
    boost::fusion::pair<keys::age, int>,
    boost::fusion::void_,
    boost::fusion::void_,
    boost::fusion::void_,
    boost::fusion::void_,
    boost::fusion::void_,
    boost::fusion::void_,
    boost::fusion::void_,
    boost::fusion::void_
>

>()'

Вот полная реализация

//             Copyright Evgeny Panasyuk 2012.
// 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)


// Reduced from larger case, some includes may not be needed

#include <boost/fusion/algorithm/transformation/transform.hpp>
#include <boost/fusion/sequence/intrinsic/value_at_key.hpp>
#include <boost/fusion/include/adapt_assoc_struct.hpp>
#include <boost/fusion/sequence/intrinsic/at_key.hpp>
#include <boost/fusion/view/transform_view.hpp>
#include <boost/fusion/view/zip_view.hpp>
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/algorithm.hpp>

#include <boost/mpl/transform.hpp>
#include <boost/mpl/vector.hpp>

#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>

#include <iostream>
#include <iterator>
#include <ostream>
#include <string>

#include <boost/mpl/push_front.hpp>
#include <boost/fusion/sequence.hpp>
#include <boost/fusion/iterator.hpp>
#include <boost/fusion/iterator/next.hpp>
#include <boost/fusion/iterator/equal_to.hpp>
#include <boost/fusion/iterator/key_of.hpp>
#include <boost/fusion/iterator/value_of.hpp>

using namespace boost;
using namespace std;

using fusion::at_key;
using fusion::at_c;

// ____________________________________________________________________________________ //

namespace res_of=boost::fusion::result_of;
using namespace boost::fusion;

template<typename Vector,typename First,typename Last,typename do_continue>
struct to_fusion_map_iter;

template<typename Vector,typename First,typename Last>
struct to_fusion_map_iter<Vector,First,Last,mpl::false_>
{
    typedef typename res_of::next<First>::type Next;
    typedef typename mpl::push_front
    <
        typename to_fusion_map_iter
        <
            Vector,
            Next,
            Last,
            typename res_of::equal_to<Next,Last>::type
        >::type,
        fusion::pair
        <
            typename res_of::key_of<First>::type,
            typename res_of::value_of_data<First>::type
        >
    >::type type;
};
template<typename Vector,typename First,typename Last>
struct to_fusion_map_iter<Vector,First,Last,mpl::true_>
{
    typedef Vector type;
};

template<typename FusionAssociativeSequence>
struct as_fusion_map
{
    typedef typename res_of::begin<FusionAssociativeSequence>::type First;
    typedef typename res_of::end<FusionAssociativeSequence>::type Last;
    typedef typename res_of::as_map
    <
        typename to_fusion_map_iter
        <
            mpl::vector<>,
            First,
            Last,
            typename res_of::equal_to<First,Last>::type
        >::type
    >::type type;
};

// ____________________________________________________________________________________ //

// Defenition of structure:

namespace demo
{
    struct employee
    {
        std::string name;
        int age;
    };
}

namespace keys
{
    struct name;
    struct age;
}

BOOST_FUSION_ADAPT_ASSOC_STRUCT
(
    demo::employee,
    (std::string, name, keys::name)
    (int, age, keys::age)
)

// ____________________________________________________________________________________ //
template<typename> void type_is();

int main()
{
    type_is<as_fusion_map<demo::employee>::type>();
}

Ответ 2

Я помнил, что видел это где-то в прошлом.

Я нашел сообщение в блоге, которое знало недостающее звено: boost::fusion::extension::struct_member_name.

Я адаптировал код из этого сообщения в блоге. Я все еще не думаю, что код чист, и у меня есть сильное чувство, что это можно сделать гораздо элегантнее. Однако сейчас вы можете сделать:

struct A {
  int a;
  typedef std::string strings[5];
  strings b;
};

BOOST_FUSION_ADAPT_STRUCT(A, (int, a)(A::strings, b))

int main() {
    using FusionDumping::dump;
    A f = { 42, { "The End Of The Universe", "Thanks For All The Fish", "Fwoop fwoop fwoop", "Don't Panic" } };
    std::cout << dump(f) << "\n";
}

Печать

STRUCT A
field a int: 42
field b ARRAY [
std::string: The End Of The Universe
std::string: Thanks For All The Fish
std::string: Fwoop fwoop fwoop
std::string: Don't Panic
std::string: 
]
ENDSTRUCT

FusionDumping - это пространство имен, которое реализует здесь деловые цели. Все, кроме функции dump(std::ostream&, T const&), является деталью реализации:

namespace FusionDumping 
{
    // everything except the `dump(std::ostream&, T const&)` function is an implementation detail
    using mangling::nameofType;

    template <typename T2> struct Dec_s;

    template <typename S, typename N> struct DecImplSeqItr_s {
        typedef typename boost::fusion::result_of::value_at<S, N>::type current_t;
        typedef typename boost::mpl::next<N>::type next_t;
        typedef boost::fusion::extension::struct_member_name<S, N::value> name_t;
        static inline std::ostream& decode(std::ostream& os, S const& s) {
            os << "field " << name_t::call() << " ";
            Dec_s<current_t>::decode(os, boost::fusion::at<N>(s));
            return DecImplSeqItr_s<S, next_t>::decode(os, s);
        }
    };

    template <typename S>
    struct DecImplSeqItr_s<S, typename boost::fusion::result_of::size<S>::type > {
        static inline std::ostream& decode(std::ostream& os, S const& s) { return os; }
    };

    template <typename S>
    struct DecImplSeqStart_s:DecImplSeqItr_s<S, boost::mpl::int_<0> > {};

    template <typename S> struct DecImplSeq_s {
    typedef DecImplSeq_s<S> type;
    static std::ostream& decode(std::ostream& os, S const& s) {
        os << "STRUCT " << nameofType(s) << "\n";
        DecImplSeqStart_s<S>::decode(os, s);
        return os << "ENDSTRUCT\n";
    };
    };

    template <typename T2> struct DecImplArray_s {
    typedef DecImplArray_s<T2> type;
    typedef typename boost::remove_bounds<T2>::type slice_t;
    static const size_t size = sizeof(T2) / sizeof(slice_t);
    static inline std::ostream& decode(std::ostream& os, T2 const& t) {
        os << "ARRAY [\n";
        for(size_t idx=0; idx<size; idx++) { Dec_s<slice_t>::decode(os, t[idx]); }
        return os << "]\n";
    }
    };

    template <typename T2> struct DecImplVoid_s {
    typedef DecImplVoid_s<T2> type;
    static std::ostream& decode(std::ostream& os, T2 const& t) { 
        return os << nameofType(t) << ": " << t << "\n";
    };
    };

    template <typename T2> struct DecCalc_s {
    typedef 
        typename boost::mpl::eval_if< traits::is_sequence<T2>, DecImplSeq_s<T2>, 
        typename boost::mpl::eval_if< boost::is_array<T2>, 
                                    boost::mpl::identity< DecImplArray_s<T2> >,
        DecImplVoid_s<T2>   > >
    ::type type;
    };

    template <typename T2> struct Dec_s : public DecCalc_s<T2>::type { };

    template <typename Data>
    struct Dumper
    {
        Dumper(Data const& data) : data(data) {}
        friend std::ostream& operator<<(std::ostream& os, const Dumper& manip) {
            return Dec_s<Data>::decode(os, manip.data);
        }
    private:
        Data const& data;
    };

    template <typename Data>
        Dumper<Data> dump(Data const& data) { return { data }; }
}

Как вы можете видеть, есть еще один свободный конец, связанный с довольно печатной печатью имен (название mangling и typeid(T).name() - это реализация). Вот реализация запаса, которая будет работать на GCC/Clang:

#ifdef DEMANGLING
#   include <cxxabi.h>
#   include <stdlib.h>

    namespace mangling {
        template <typename T> std::string nameofType(const T& v) {
            int     status;
            char   *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status);
            std::string name(realname? realname : "????");
            free(realname);

            return name;
        }
    }
#else
    namespace mangling {
        template <typename T> std::string nameofType(const T& v) {
            return std::string("typeid(") + typeid(v).name() + ")";
        }
    }
#endif

Полный список программ

Интегрируя все это, посмотрите Live On Coliru

Для справок в будущем

#include <typeinfo>
#include <string>
#include <iostream>
#include <boost/fusion/include/sequence.hpp>
#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/is_sequence.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/type_traits.hpp> // is_array, is_class, remove_bounds

#define DEMANGLING

#ifdef DEMANGLING
#   include <cxxabi.h>
#   include <stdlib.h>

    namespace mangling {
        template <typename T> std::string nameofType(const T& v) {
            int     status;
            char   *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status);
            std::string name(realname? realname : "????");
            free(realname);

            return name;
        }
    }
#else
    namespace mangling {
        template <typename T> std::string nameofType(const T& v) {
            return std::string("typeid(") + typeid(v).name() + ")";
        }
    }
#endif

namespace FusionDumping 
{
    // everything except the `dump(std::ostream&, T const&)` function is an implementation detail
    using mangling::nameofType;

    template <typename T2> struct Dec_s;

    template <typename S, typename N> struct DecImplSeqItr_s {
        typedef typename boost::fusion::result_of::value_at<S, N>::type current_t;
        typedef typename boost::mpl::next<N>::type next_t;
        typedef boost::fusion::extension::struct_member_name<S, N::value> name_t;
        static inline std::ostream& decode(std::ostream& os, S const& s) {
            os << "field " << name_t::call() << " ";
            Dec_s<current_t>::decode(os, boost::fusion::at<N>(s));
            return DecImplSeqItr_s<S, next_t>::decode(os, s);
        }
    };

    template <typename S>
    struct DecImplSeqItr_s<S, typename boost::fusion::result_of::size<S>::type > {
        static inline std::ostream& decode(std::ostream& os, S const& s) { return os; }
    };

    template <typename S>
    struct DecImplSeqStart_s:DecImplSeqItr_s<S, boost::mpl::int_<0> > {};

    template <typename S> struct DecImplSeq_s {
    typedef DecImplSeq_s<S> type;
    static std::ostream& decode(std::ostream& os, S const& s) {
        os << "STRUCT " << nameofType(s) << "\n";
        DecImplSeqStart_s<S>::decode(os, s);
        return os << "ENDSTRUCT\n";
    };
    };

    template <typename T2> struct DecImplArray_s {
    typedef DecImplArray_s<T2> type;
    typedef typename boost::remove_bounds<T2>::type slice_t;
    static const size_t size = sizeof(T2) / sizeof(slice_t);
    static inline std::ostream& decode(std::ostream& os, T2 const& t) {
        os << "ARRAY [\n";
        for(size_t idx=0; idx<size; idx++) { Dec_s<slice_t>::decode(os, t[idx]); }
        return os << "]\n";
    }
    };

    template <typename T2> struct DecImplVoid_s {
    typedef DecImplVoid_s<T2> type;
    static std::ostream& decode(std::ostream& os, T2 const& t) { 
        return os << nameofType(t) << ": " << t << "\n";
    };
    };

    template <typename T2> struct DecCalc_s {
    typedef 
        typename boost::mpl::eval_if<boost::fusion::traits::is_sequence<T2>, DecImplSeq_s<T2>, 
        typename boost::mpl::eval_if< boost::is_array<T2>, 
                                    boost::mpl::identity< DecImplArray_s<T2> >,
        DecImplVoid_s<T2>   > >
    ::type type;
    };

    template <typename T2> struct Dec_s : public DecCalc_s<T2>::type { };

    template <typename Data>
    struct Dumper
    {
        Dumper(Data const& data) : data(data) {}
        friend std::ostream& operator<<(std::ostream& os, const Dumper& manip) {
            return Dec_s<Data>::decode(os, manip.data);
        }
    private:
        Data const& data;
    };

    template <typename Data>
        Dumper<Data> dump(Data const& data) { return { data }; }
}

struct A {
  int a;
  typedef std::string strings[5];
  strings b;
};

BOOST_FUSION_ADAPT_STRUCT(A, (int, a)(A::strings, b))

int main() {
    using FusionDumping::dump;
    A f = { 42, { "The End Of The Universe", "Thanks For All The Fish", "Fwoop fwoop fwoop", "Don't Panic" } };
    std::cout << dump(f) << "\n";
}