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::pair
s: 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.
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).
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>();
}
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";
}
来源:https://stackoverflow.com/questions/19657065/is-it-possible-to-generate-a-fusion-map-from-an-adapted-struct