Boost.Variant, Boost.MPL: How to append types?

2019-03-05 05:36发布

问题:

I look at this grate code based on boost.Any and cant help but wonder if we could use Boost.Variant instead. I wonder if such API would be possible:

void voidFunc()
{
    std::cout << "void called" << std::endl;
}

int stringFunc(std::string str)
{
    std::cout << str << std::endl;
    return 0;
}

int main()
{
    some_map_like_type<std::string, boost::variant> funcs;
    funcs.insert<void , void >("voidFunc", &voidFunc)); // now our variant vould contain something like boost::function<void, void>
    funcs.insert<int , std::string>("stringFunc", &stringFunc)); // and now we added to our variant a new type: boost::function<int , std::string>
    funcs.insert<void , void >("voidFunc2", &voidFunc)); // and now our variant should not change because it already contains boost::function<void, void> type


    // And here when all the fun part is:
    funcs["voidFunc"](); // compiles
    funcs["stringFunc"]("hello"); // compiles
    funcs["stringFunc"](some_not_std_string_class); // does not compile.
    return 0;
}

That means that at the end compiler would have to compile something like:

void voidFunc()
{
    std::cout << "void called" << std::endl;
}

int stringFunc(std::string str)
{
    std::cout << str << std::endl;
    return 0;
}

int main()
{
    some_map_like_type<std::string, boost::variant< boost::function<void , void>, boost::function<int , std::string> > > funcs;
    funcs.insert<void , void >("voidFunc", &voidFunc)); // now our variant vould contain something like boost::function<void, void>
    funcs.insert<int , std::string>("stringFunc", &stringFunc)); // and now we added to our variant a new type: boost::function<int , std::string>
    funcs.insert<void , void >("voidFunc2", &voidFunc)); // and now our variant should not change because it already contains boost::function<void, void> type


    // And here when all the fun part is:
    funcs["voidFunc"](); // compiles
    funcs["stringFunc"]("hello"); // compiles
    funcs["stringFunc"](some_not_std_string_class); // here it would give error and would not compile
    return 0;
}

Update:

What have I tried (based on this Variant docs and this MPL demos and docs):

#include <boost/static_assert.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/multiplies.hpp>
#include <boost/mpl/placeholders.hpp>

#include <boost/variant.hpp>
#include <iostream>
#include <string>
#include <vector>

class sudo_science
{
public:
    typedef  boost::mpl::vector_c<int> types_vector1;

    typedef boost::make_recursive_variant< types_vector1 >::type recursive_variant_t;

    std::vector< recursive_variant_t > variant_seq;

    template <typename T>
    void append(T val)
    {
        typedef  boost::mpl::push_back<types_vector1,T>::type types_vector1;
        variant_seq.push_back(val);
        return;
    }

    std::vector< recursive_variant_t > give_me_end_variant()
     {
         return variant_seq;
     }
};

int main()
{
    sudo_science a;
    a.append<float>(1.0);
    a.append<std::string>("Stack and Boost");

    //sorry for C++11
    auto varint = a.give_me_end_variant();

    return 0;
}

But it fails to compile with 2 same errors:

Error   1   error C2665: 'boost::detail::variant::make_initializer_node::apply<BaseIndexPair,Iterator>::initializer_node::initialize' : none of the 2 overloads could convert all the argument types    c:\program files\boost\include\boost\variant\variant.hpp    1330    1

回答1:

It is not possible. operator[] is a run-time thing, while types are a compile-time thing. So should the compiler compile the following?

char const* str;
if (some_condition())
  str = "voidFunc";
else
  str = "stringFunc";
// ... some more code
if (some_condition())
  funcs[str]();
else
  funcs[str](str);

How is the compiler supposed to know whether the second call to some_condition() gives the same result as before? Or whether the code in between modified the value of str?

What about the following:

void call(some_map_like_type<std::string, boost::variant> const& funcs)
{
  funcs["voidFunc"]();
}

How is the compiler supposed to know whether at call time funcs contains an entry mapping "voidFunc"to a function with no arguments? And what should happen if it is called once on with a value that does, and once with a value which doesn't?

Depending on what you actually want to achieve, there might be a way to get it with templates and constexpr functions. However note that nothing which happens at runtime can affect whether the code compiles, for the simple reason that the code cannot be run before it is compiled.