Imagine we want to model C struct with dynamic C++ types. I.e. we have a set of fields, each field has a name and a value. The value can be a simple primitive type (let's say just int
for the sake of the example) or another structure, i.e. another set of fields.
It's quite straightforward:
template <typename RecursiveVariant>
struct Field
{
std::string name;
RecursiveVariant value;
};
template <typename RecursiveVariant>
using StructFields = std::vector<Field<RecursiveVariant>>;
typedef typename boost::make_recursive_variant<
int
, StructFields<boost::recursive_variant_>
>::type FieldValue;
int main(int argc, char* argv[])
{
FieldValue fv = 2;
StructFields<FieldValue> sf;
Field<FieldValue> f{"name", 2};
sf.push_back(f);
fv = sf;
return 0;
}
It works as expected. However, if we try to use multi_index_container
instead of std::vector
, it won't compile. If I change the definition of StructFields
to this:
template <typename RecursiveVariant>
using StructFields = multi_index_container<
Field<RecursiveVariant>
, indexed_by<
sequenced<>
, ordered_unique<
member<
Field<RecursiveVariant>
, std::string
, &Field<RecursiveVariant>::name
>
>
>
>;
The compiler (MSVC from VS 15.6.3) will issue
binary '=': no operator found which takes a right-hand operand of type 'boost::multi_index::multi_index_container,boost::multi_index::multi_index_container, ...
complaining on line fv = sf
. It complains on ambiguity between variant& operator=(const variant& rhs)
and variant& operator=(variant&& rhs)
. I am not sure how these operator=
are even involved. Is there any recipe to fix this?
Like @sehe, I suspect the problem has to do with Boost.MPL magic (in particular, recognition of so-called placeholder expressions) somehow failing, but don't know why.
FWIW, replacing the using StructFields
bit with a hard type definition seems to solve the issue:
template <typename RecursiveVariant>
struct StructFields: multi_index_container<
Field<RecursiveVariant>
, indexed_by<
sequenced<>
, ordered_unique<
member<
Field<RecursiveVariant>
, std::string
, &Field<RecursiveVariant>::name
>
>
>
>{};
or, better yet, do the hard-type trick only on the indices:
template <typename RecursiveVariant>
struct StructFieldsIndices:indexed_by<
sequenced<>
, ordered_unique<
member<
Field<RecursiveVariant>
, std::string
, &Field<RecursiveVariant>::name
>
>
>{};
template <typename RecursiveVariant>
using StructFields = multi_index_container<
Field<RecursiveVariant>
, StructFieldsIndices<RecursiveVariant>
>;
Postscript: OK, I think I know what's going on. As previously noted, no problem arises when using "simpler" indices such as sequenced
or random_access
, it is when throwing ordered_unique
in that things fail. These three are index specifiers declared as follows:
template <typename TagList=tag<> >
struct sequenced;
template <typename TagList=tag<> >
struct random_access;
template<typename Arg1,typename Arg2=mpl::na,typename Arg3=mpl::na>
struct ordered_unique;
The peculiarity with ordered_unique
is its mpl::na
defaults: when in the context of defining StructFields<boost::recursive_variant_>
, Boost.MPL "sees" those mpl::na
s through the ordered_unique
layer and fails to recognize the whole type as a placeholder expression with one arg.
Post-postscript: I now really think what's going on :-) and it isn't related to mpl::na
(in fact, sequenced
has concealed mpl::na
s in its default tag<>
= tag<mp::na,...,mpl::na>
argument and raises no error). The problem has to do with the &Field<RecursiveVariant>::name
arg in member
and the inability of Boost.MPL to substitute &Field<FieldValue>::name
for &Field<boost::recursive_variant_>::name
when processing the placeholder expression (I guess because this is a non-type template parameter). So, you can restrict the hard-type trick to just the definition of member
as follows:
template <typename RecursiveVariant>
struct FieldName: member<
Field<RecursiveVariant>
, std::string
, &Field<RecursiveVariant>::name
>{};
template <typename RecursiveVariant>
using StructFields = multi_index_container<
Field<RecursiveVariant>
, indexed_by<
sequenced<>
, ordered_unique<FieldName<RecursiveVariant>>
>
>;