Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to generate a fusion map from an adapted struct?

Let A be:

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

I would like to generate a fusion::map from the struct such that it contains the fusion::pairs: fusion::pair<A::keys::a, int> and fusion::pair<A::keys::b, std::string>. Something like

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

I've tried with BOOST_FUSION_ADAPT_ASSOC_STRUCT

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

)

This adapts A to be used as an associative sequence, but I haven't found a way to construct a map from it. In particular, if I iterate over it I get only the values. If I could iterate over the keys that would be really helpful, because then I could zip the values and the keys to build a map, but I haven't found a way to do this yet.

like image 636
gnzlbg Avatar asked Oct 29 '13 11:10

gnzlbg


2 Answers

You should use Associative Iterator interface - it provides result_of::key_of<I>::type metafunciton.

I have found code in my old records, and extracted relevant part. I used it during implementation of SoA vector (as I understand from chat - you are implementing it too), but eventually switched to just explicit definition of fusion::map. I think I did that in order to have unified interface of normal structure and structure of references (i.e. both are accessed via type tag).

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>();
}

Result is:

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_
>

>()'

Here is full implementation

//             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>();
}
like image 123
Evgeny Panasyuk Avatar answered Oct 10 '22 07:10

Evgeny Panasyuk


I remembered having seen this somewhere in the past.

I've found a blog post that knew the missing link: boost::fusion::extension::struct_member_name.

I've adapted the code from that blog post. I still don't think the code is clean, and have the strong feeling this can be done much more elegantly. However, right now you can do:

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";
}

Prints:

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 is the namespace that implements the business end of things here. Everything besides the dump(std::ostream&, T const&) function is an implementation detail:

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 }; }
}

As you can see, there's one more loose end, related to pretty printing the typenames (name mangling and typeid(T).name() are implementation defined). Here's a stock implementation that will work on 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

Full program listing

Integrating it all, see it Live On Coliru

For future reference

#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";
}
like image 31
sehe Avatar answered Oct 10 '22 08:10

sehe