Partially specialize methods of variadic template classes

后端 未结 2 825
一整个雨季
一整个雨季 2021-01-25 23:54

I want to write an unmarshaller to extract arguments stored in a msgpack array for individual arguments to a call of sigc::signal::emit(...). I tried this:

2条回答
  •  南笙
    南笙 (楼主)
    2021-01-26 00:08

    In C++14:

    template
    auto foreach( F&& f ) {
      return [f=std::forward(f)](auto&&...args)mutable{
        using discard=int[];
        (void)discard{0,(void(
          f(decltype(args)(args))
        ),0)...};
      };
    }
    template
    auto index_over( std::index_sequence ) {
      return [](auto&& f)->decltype(auto){
        return decltype(f)(f)( std::integral_constant{}... );
      };
    }
    template
    auto index_upto( std::integral_constant ={} ) {
      return index_over( std::make_index_sequence{} );
    }
    
    
    template
    void MsgpackAdapter::do_emit (const msgpack::object_array &mp_args)
    {
      std::tuple args;
      index_upto()(
        foreach(
          [&](auto I){
            mp_args.ptr[I].convert(std::get(args));
          }
        )
      );
      index_upto()(
        [&](auto...Is){
          signal_.emit(std::get(args)...);
        }
      );
    }
    

    or somesuch. Live example.

    Basically, make a tuple.

    Create a pack of indexes into that tuple.

    For each index into the tuple, call convert.

    Then, call emit getting each element of the tuple.

    There are many examples of code on stack overflow that involve passing each argument of a tuple to a function call. That is the emit part.

    There are many examples on stack overflow of doing something for each element of a tuple. Doing so with an index is a bit trickier, but at worst you can count if the for each element does things in order.

    These can be done in C++11, but in C++14 I can do it all in the function without helper functions.


    Description of the above magic code. index_upto returns a lambda. This lambda takes another lambda, then calls it with compile time constants from 0 up to N-1. It does this by calling index_over, which takes a list of indexes.

    foreach takes a lambda f. It then returns a lambda that takes any number of arguments, and calls the f once with each one of those arguments. Its implementation is a bit deep mojo involving parameter packs and array initialization.

    Composing index_upto and foreach lets you do something for each compile-time value from 0 to N-1. This is how we call .convert.

    Just calling index_upto lets us pass all of the arguments at once to emit.


    We can do something similar in C++11, but we'd instead write helper functions that take parameter packs and such. It is more than a bit of a pain.

提交回复
热议问题