Boost fusion oddity
I am trying out Fusion and found something very odd... Here is the code... I have highlighted the problematic code with // ############ TROUBLE HERE ######
#include <tr1/cstdint>
#include <tr1/functional>
#include <string>
#include <iostream>
// #define FUSION_MAX_VECTOR_SIZE 64
#define BOOST_MPL_LIMIT_STRING_SIZE 128
#include <boost/type_traits.hpp>
#include <boost/mpl/string.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/tuple.hpp>
#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/container/generation.hpp>
#include <boost/fusion/container/generation/vector_tie.hpp>
typedef std::tr1::int32_t int32;
typedef std::tr1::int64_t int64;
template < class type_const_ref >
struct remove_const_reference
{
typedef typename boost::remove_reference < type_const_ref >::type type_const;
typedef typename boost::remove_const < type_const >::type type;
};
template < class T >
class MetaClass;
namespace fusion = boost::fusion;
template < class T >
struct ConstRefFieldMap
{
typedef typename MetaClass < T >::FieldNames FieldNames;
typedef typename MetaClass < T >::ConstRefFields ConstRefFields;
typedef typename boost::fusion::result_of::zip < FieldNames const, ConstRefFields const >::type type;
};
template < class T >
static typename MetaClass < T >::FieldNames fieldNames()
{
return typename MetaClass < T >::FieldNames();
}
template < class T >
static typename MetaClass < T >::ConstRefFields constRefFields(T const &obj)
{
return MetaClass < T >::constRefFields(obj);
}
template < class T >
static typename ConstRefFieldMap < T >::type const constRefFieldMap(T const &obj)
{
return boost::fusion::zip(fieldNames < T >(), constRefFields(obj));
}
class Currency
{
private:
typedef MetaClass < Currency > Meta;
friend class MetaClass < Currency >;
private:
std::string m_isoCode;
int32 m_rank;
public:
Currency(std::string const &isoCode, int32 const rank)
: m_isoCode(isoCode)
, m_rank(rank)
{
}
std::string const& getIsoCode() const
{
return m_isoCode;
}
int32 const getRank() const
{
return m_rank;
}
private:
void setIsoCode(std::string const &isoCode)
{
m_isoCode = isoCode;
}
public:
void setRank(int32 rank)
{
m_rank = rank;
}
};
template <>
class MetaClass < Currency >
{
public:
typedef Currency data_type;
public:
typedef std::string IsoCodeType;
typedef int32 RankType;
typedef boost::fusion::vector <
boost::mpl::string < 'i', 's', 'o', 'C', 'o', 'd', 'e' >
, boost::mpl::string < 'r', 'a', 'n', 'k' >
> FieldNames;
typedef boost::fusion::vector <
IsoCodeType &
, RankType &
> MutableRefFields;
typedef boost::fusion::vector <
IsoCodeType const &
, RankType const &
> ConstRefFields;
static MutableRefFields mutableRefFields(Currency &obj)
{
return MutableRefFields(obj.m_isoCode, obj.m_rank);
}
static ConstRefFields constRefFields(Currency const &obj)
{
return ConstRefFields(obj.m_isoCode, obj.m_rank);
}
};
template < class T, class U >
static typename ConstRefFieldMap < T >::type const constRefFieldMapTest(T const &obj, U const &u)
{
return boost::fusion::zip(fieldNames < T >(), u);
}
int main()
{
Currency const EUR("EUR", 500);
using boost::fusion::any;
{
std::cout << boost::fusion::at_c < 0 >(constRefFields(EUR)) << " : " << boost::fusion::at_c < 1 >(constRefFields(EUR)) << std::endl;
ConstRefFieldMap < Currency >::type const &fm = boost::fusion::zip(fieldNames < Currency >(), constRefFields(EUR));
// ############ TROUBLE HERE ######
// ConstRefFieldMap < Currency >::type const &fm = constRefFieldMap(EUR);
// ############ TROUBLE HERE ######
{
{
typedef boost::fusion::result_of::at_c < ConstRefFieldMap < Currency >::type, 0 >::type field_value_type;
field_value_type const v = boost::fusion::at_c < 0 >(fm);
typedef boost::fusion::result_of::at_c < field_value_type, 0 >::type field_name_type;
field_name_type const n = boost::fusion::at_c < 0 >(v);
typedef boost::fusion::result_of::at_c < field_value_type, 1 >::type field_data_type;
field_data_type const d = boost::fusion::at_c < 1 >(v);
std::cout << boost::mpl::c_str < remove_const_reference < field_name_type >::type >::value << " : " << d << std::endl;
}
{
typedef boost::fusion::result_of::at_c < ConstRefFieldMap < Currency >::type, 1 >::type field_value_type;
field_value_type const v = boost::fusion::at_c < 1 >(fm);
typedef boost::fusion::result_of::at_c < field_value_type, 0 >::type field_name_type;
field_name_type const n = boost::fusion::at_c < 0 >(v);
typedef boost::fusion::result_of::at_c < field_value_type, 1 >::type field_data_type;
field_data_type const d = boost::fusion::at_c < 1 >(v);
std::cout << boost::mpl::c_str < remove_const_reference < field_name_type >::type >::value << " : " << d << std::endl;
}
}
}
}
I get garbage values or SIGSEGV if I use the constRefFieldMap() function. If I call boost::fusion::zip directly it works perfectly. Here is the output...
EUR : 500
isoCode : EUR
rank : 500
I have looked at this question earlier... am I running into the same problem here???
EDIT 1:
Presenting an example of what I am trying to do...
开发者_如何学JAVAActually... I am trying to write code like this.
MetaObject < Currency const > EUR_META(make_meta_object(EUR));
std::cout << get_field < std::string >("isoCode", EUR_META.constRefFieldMap()) << std::endl;
MetaObject < Currency > GBP_META(make_meta_object(GBP));
MutableRefFieldMap < Currency >::type const &fm = GBP_META.mutableRefFieldMap();
std::cout << set_field("rank", fm, 497) << std::endl;
Accessors and modifiers that I can invoke by field names...
I plan to write a spirit parser to parse JSON & XML and create objects... with some help from my code generators. The main idea is to avoid generating the parsing code for every object, but only for those objects that get used and hence reduce the binary size. I have 1000s of objects now.
I have got this working now.
It's unfortunate this question didn't draw more attention, but I guess the large chunk of code didn't help much (that and the fact that template metaprogramming is not so popular).
Anyway, you're right, it's a similar issue.
The problem is that the views in fusion do not copy the arguments, they only keep references to them. This allows you to modify the original arguments by their intermediate.
The trouble is that in C++ you are authorized to bind a temporary to a const-reference, and the temporary lifetime is extended to that of the reference. However this behavior is not transitive, which has caused a heapload of troubles. (Clang will attempt to diagnose those situations, unfortunately the first patch failed :p)
So here your problem is located in one single line:
return boost::fusion::zip(fieldNames < T >(), constRefFields(obj));
fieldNames<T>()
creates a temporary, that is bound to a const-reference, it's lifetime is extended until the end of the expression:;
- when you return the view, the temporary lifetime has expired, you're holding onto a dangling reference
Quick fix: make fieldNames<T>()
have a local static variable and return a reference to this variable, this will fix the lifetime issue.
I still haven't understood what you were attempting though, so I can't really give "more sensible" advice :)
精彩评论