部分专门化可变参数模板类的方法

时间:2017-03-21 17:18:02

标签: c++ c++11 variadic-templates unmarshalling template-specialization

我想编写一个unmarshaller来提取存储在msgpack数组中的参数,用于调用sigc::signal::emit(...)的各个参数。我试过这个:

template<class... T> class MsgpackAdapter: public MsgpackAdapterBase {
public:
    MsgpackAdapter (sigc::signal<void, T...> &sig)
            : MsgpackAdapterBase (), signal_ (sig)
    {}
protected:
    virtual void do_emit (const msgpack::object_array &mp_args) override;
private:
    sigc::signal<void, T...> signal_;
};

template<class T1>
void MsgpackAdapter<T1>::do_emit (const msgpack::object_array &mp_args)
{
    T1 a1;
    mp_args.ptr[0].convert (a1);
    signal_.emit (a1);
}

template<class T1, class T2>
void MsgpackAdapter<T1, T2>::do_emit (const msgpack::object_array &mp_args)
{
    T1 a1;
    T2 a2;
    mp_args.ptr[0].convert (a1);
    mp_args.ptr[1].convert (a2);
    signal_.emit (a1, a2);
}

以及最多4个参数。但我收到此错误消息(来自使用clang 3.9的vim-youcompleteme):

'MsgpackAdapter<T1>::' for declaration does not refer into a class, class template or class template partial specialization  

似乎我可以对整个班级进行部分专业化:

template<class T1> class MsgpackAdapter<T1>: public MsgpackAdapterBase { ... };

但我宁愿能够专注于emit方法来减少复制量和放大倍数。粘贴代码。我错过了明显的东西吗?我认为主要的困难是do_emit没有采用模板化的论点。

另一个好奇心是,如果我在没有可变参数模板的情况下尝试这样做,并使用:

template<class T1> class MsgpackAdapter: public MsgpackAdapterBase { ... };
template<class T1, class T2> class MsgpackAdapter: public MsgpackAdapterBase { ... };

我收到第二个类定义与第一个类冲突的错误。这是可以理解的,但我想知道sigc如何在没有可变参数模板的情况下管理类似的东西。

2 个答案:

答案 0 :(得分:2)

我一直觉得专门用于成员函数的最可维护的方法是遵循一个专门的函数对象:

Imports Microsoft.Office.Interop

Public Class Form1

    Public WithEvents myIns As Outlook.Inspector

    Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load

        Process.Start("Outlook.exe")
        Threading.Thread.Sleep(3000)
        Dim olApp As New Outlook.Application

        Dim myMailItem As Outlook.MailItem
        myMailItem = CType(olApp.CreateItem(Outlook.OlItemType.olMailItem), Outlook.MailItem)
        myMailItem.Subject = "Hello"
        myMailItem.To = "anybody@example.com"
        myMailItem.Body = "Hi there..."

        Dim myIns As Outlook.Inspector
        myIns = myMailItem.GetInspector

        myIns.Display(False)

        Dim myWord As Word.Document
        myWord = CType(myIns.WordEditor, Word.Document)

        Dim mySel As Word.Selection
        mySel = myWord.Application.Selection

        Threading.Thread.Sleep(10000)

        'Following line just for testing. Normally following line doesnt exist in my original code. The real scenario is the user minimizes the inspector manually.
        myIns.WindowState = Outlook.OlWindowState.olMinimized

        myWord.InlineShapes.AddPicture(FileName:="C:\Example.png", LinkToFile:=False, SaveWithDocument:=True, Range:=mySel.GoTo(What:=Word.WdGoToItem.wdGoToLine, Which:=Word.WdGoToDirection.wdGoToLast, Count:=-4))

        'myMailItem.Send()

    End Sub

End Class

答案 1 :(得分:1)

In C++14:

template<class F>
auto foreach( F&& f ) {
  return [f=std::forward<F>(f)](auto&&...args)mutable{
    using discard=int[];
    (void)discard{0,(void(
      f(decltype(args)(args))
    ),0)...};
  };
}
template<std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
  return [](auto&& f)->decltype(auto){
    return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
  };
}
template<std::size_t N>
auto index_upto( std::integral_constant<std::size_t, N> ={} ) {
  return index_over( std::make_index_sequence<N>{} );
}


template<class...Ts>
void MsgpackAdapter<Ts...>::do_emit (const msgpack::object_array &mp_args)
{
  std::tuple<Ts...> args;
  index_upto<sizeof...(Ts)>()(
    foreach(
      [&](auto I){
        mp_args.ptr[I].convert(std::get<I>(args));
      }
    )
  );
  index_upto<sizeof...(Ts)>()(
    [&](auto...Is){
      signal_.emit(std::get<Is>(args)...);
    }
  );
}

or somesuch. Live example的指示。

基本上,做一个元组。

在该元组中创建一组索引。

对于元组中的每个索引,请调用convert。

然后,调用emit获取元组的每个元素。

有很多关于堆栈溢出的代码示例,涉及将元组的每个参数传递给函数调用。那是发射部分。

关于为元组的每个元素做某事的堆栈溢出有很多例子。使用索引这样做有点棘手,但在最坏的情况下,你可以计算每个元素是否按顺序执行。

这些可以在C ++ 11中完成,但在C ++ 14中,我可以在没有辅助函数的函数中完成所有操作。

上述魔术代码的描述。 index_upto返回一个lambda。这个lambda接受另一个lambda,然后用编译时常量从0到N-1调用它。它通过调用index_over执行此操作,该foreach采用索引列表。

index_upto需要一个lambda f。然后它返回一个带有任意数量参数的lambda,并用这些参数中的每一个调用f一次。它的实现是一个涉及参数包和数组初始化的深度mojo。

通过撰写foreach0,您可以为N-1.convert的每个编译时值执行某些操作。我们称之为index_upto

只需致电emit,我们就可以立即将所有参数传递给ssh-add -A

我们可以在C ++ 11中做类似的事情,但我们会编写带有参数包等的辅助函数。这不仅仅是一种痛苦。