功能对象的包装器,用于更改参数和返回值类型

时间:2018-05-03 20:03:36

标签: c++ c++11 templates

我想编写C ++类(让类型名称为Adapter),它在构造函数中接受函数指针或函数对象(特别是lambda函数)并在内部按值存储它(如std :: function for为例)。在构造类之后,它也表示功能对象,并且作为内部功能对象的包装器。但是有以下条件:

  1. 包装类(适配器)的返回类型始终是预定义的(让它为ReturnType);
  2. 但内部仿函数可能没有返回值 - 在这种情况下,一些DefaultValue被替换;
  3. 包装类总是接收一些ArgType作为operator();
  4. 的参数
  5. 但内部函数也可能接收ArgType作为参数,或者根本不接受参数 - 在后一种情况下,它应该不带参数调用。
  6. 第一个问题 - 它可能与C ++ 11一起使用,第二个问题我怎么能这样做?我不知道我应该从哪里开始。

    我需要推断(在模板中)函数对象的返回值吗?第一个陷阱是,std :: result_of不支持函数指针。我不知道如何推断功能对象的参数类型(可能是空的,没有参数或ArgType)(我知道函数指针是如何通过模板化结构实现的,但我怎样才能为函数对象做到这一点) ?)。

    我需要这样的东西:

    template <typename Functor?> class Adapter
    {
    private:
        std::function<Void_or_ReturnType (Void_or_ArgType)> f;
    
    public:
        template <typename Functor?> Adapter(const Functor& f) { ??? }
    
        ReturnType operator()(ArgType arg)
        {
             // one of the following four variants:
             return f(arg);
             f(arg); return DefaultValue;
             return f();
             f(); return DefaultValue;
      }
    };
    

    可能需要在模板中推导出Void_or_ReturnType和Void_or_ArgType类型,并为四种情况中的每一种为operator()编写不同的模板特化。但我怎么能这样做呢?

2 个答案:

答案 0 :(得分:0)

我花了几个小时就找到了一些解决方案: http://coliru.stacked-crooked.com/a/628b8971466fa74d

不确定,如果这是一个好的解决方案。可能有人建议我,我怎样才能最小化这段代码。

我在这里复制源代码以防万一(我在coliru.stacked-crooked.com上更新了源码,这里是旧版本,请看上面的链接):

#include <functional>
#include <stdio.h>

// functors
template<class T, class Yes, class No> struct IsArgs   { typedef typename IsArgs  <decltype(&T::operator()), Yes, No>::type type; };
template<class T, class Yes, class No> struct IsResult { typedef typename IsResult<decltype(&T::operator()), Yes, No>::type type; };

// function pointers
template<class R, class Yes, class No, typename ...Args> struct IsArgs   <R (*)(Args...), Yes, No>    { typedef Yes type; };
template<class R, class Yes, class No>                   struct IsArgs   <R (*)(), Yes, No>           { typedef No  type; };
template<class Yes, class No, class R, typename ...Args> struct IsResult <R (*)(Args...), Yes, No>    { typedef Yes type; };
template<class Yes, class No, typename ...Args>          struct IsResult <void (*)(Args...), Yes, No> { typedef No  type; };

// for function refs
template<class R, class Yes, class No, typename ...Args>    struct IsArgs  <R (Args...), Yes, No>     { typedef Yes type; };
template<class R, class Yes, class No>                      struct IsArgs  <R (), Yes, No>            { typedef No  type; };
template<class Yes, class No, typename R, typename ...Args> struct IsResult <R (Args...), Yes, No>    { typedef Yes type; };
template<class Yes, class No, typename ...Args>             struct IsResult <void (Args...), Yes, No> { typedef No  type; };

// for member pointers (lambdas, functors)
template<class T, class Yes, class No, class R, typename ...Args> struct IsArgs   <R (T::*)(Args...) const, Yes, No>    { typedef Yes type; };
template<class T, class Yes, class No, class R>                   struct IsArgs   <R (T::*)() const, Yes, No>           { typedef No  type; };
template<class T, class Yes, class No, class R, typename ...Args> struct IsResult <R (T::*)(Args...) const, Yes, No>    { typedef Yes type; };
template<class T, class Yes, class No, typename ...Args>          struct IsResult <void (T::*)(Args...) const, Yes, No> { typedef No  type; };

template <typename Retval, Retval Default, typename ...Args> class Adapter
{
    struct Noargs {
        std::function<Retval ()> func;
        template <typename F> Noargs(const F& f) : func(f) {}
        Retval operator()(Args...) { return func(); }
    };

    struct Noreturn {
        std::function<void (Args...)> func;
        template <typename F> Noreturn(const F& f) : func(f) {}
        Retval operator()(Args...args) { return func(args...), Default; }
    };

    struct Noretargs {
        std::function<void ()> func;
        template <typename F> Noretargs(const F& f) : func(f) {}
        Retval operator()(Args...) { return func(), Default; }
    };

    std::function<Retval (Args...args)> func;

public:
    template <typename Functor> Adapter(const Functor &f)
        : func(typename IsArgs<Functor,
                typename IsResult<Functor, std::function<Retval (Args...)>, Noreturn>::type, 
                typename IsResult<Functor, Noargs, Noretargs>::type>::type(f)) {}

    Retval operator()(Args...args) const { return func(args...); }
};




void deaf_silent_f() { puts("deaf/silent"); }
void silent_f(const char *arg) { printf("silent %s\n", arg); }
const char *deaf_f() { puts("deaf"); return "deaf"; }
const char *normal_f(const char *arg) { printf("normal %s\n", arg); return "normal"; }

const char Default[] = "DEFAULT";

int main(int argc, char *argv[])
{    
    typedef Adapter<const char*, Default, const char*> A;

    {
      puts("function refs");
      A ds(deaf_silent_f); printf("-> %s\n", ds("ds"));
      A s(silent_f); printf("-> %s\n", s("s"));
      A d(deaf_f); printf("-> %s\n", d("d"));
      A n(normal_f); printf("-> %s\n", n("n"));
      puts("");
    }

    {
      puts("function pointers");
      A ds(&deaf_silent_f); printf("-> %s\n", ds("ds"));
      A s(&silent_f); printf("-> %s\n", s("s"));
      A d(&deaf_f); printf("-> %s\n", d("d"));
      A n(&normal_f); printf("-> %s\n", n("n"));
      puts("");
    }

    {
       puts("functors");
      A ds([=]{ printf("deaf/silent %d\n", argc); });         printf("-> %s\n", ds("ds"));
      A s([=](const char *a){ printf("silent(%s) %d\n", a, argc); });           printf("-> %s\n", s("s"));
      A d([=]{ printf("deaf %d\n", argc); return "deaf"; });  printf("-> %s\n", d("d"));
      A n([=](const char *a){ printf("normal(%s) %d\n", a, argc); return "normal";}); printf("-> %s\n", n("n"));
    }

    return 0;
}

答案 1 :(得分:0)

嗯......你问的问题并非琐碎......

首先:我认为将默认返回值作为模板参数是一个好主意:适用于整数类型,但是,例如,不适用于浮点类型。你可以解决这个问题,但我建议在构造函数中将默认值作为值传递。

第二:我建议使用以下代码来检测F类型的可调用对象是否可以使用给定的类型列表进行调用

template <typename ...>
constexpr std::false_type isInvocableWithHelper (long);

template <typename F, typename ... Args>
constexpr auto isInvocableWithHelper (int)
   -> decltype( std::declval<F>()
                   (std::forward<Args>(std::declval<Args>())...),
                std::true_type{} );

template <typename F, typename ... Args>
using isInvocableWith = decltype(isInvocableWithHelper<F, Args...>(0));

现在,使用委托构造函数和标记分派,您的Adapter构造函数可以

  template <typename F>
  Adapter (F const & f, RetT defVal = RetT{})
     : Adapter{f, defVal, isInvocableWith<F, Args...>{},
               isInvocableWith<F>{}}
   { }

其中第三个参数(isInvocableWith<F, Args...>{})为真(std::true_type)仅当F对象可以使用Args...个对象列表和第四个参数({{仅当isInvocableWith<F>{}对象在没有参数的情况下可调用时,{1}})才为真(std::true_type)。

现在您必须检测F(类型为F的对象)是否返回F或值(可能是void或可转换为RetT的内容})。

您可以再次使用委托构造函数和使用

进行标记分派
RetT

如果可以使用 template <typename F> Adapter (F const & f, RetT const & defVal, std::true_type const &, std::false_type const &) : Adapter{f, defVal, std::true_type{}, std::false_type{}, std::is_same<void, decltype(f(std::forward<Args>(std::declval<Args>())...))>{}} { } 参数进行调用(如果Args...返回std::true_type,则观察最后一个参数是std::true_type(继承自f)。< / p>

如果void无参数可调用,则构造函数变为

f

现在是四个最终的构造函数

  template <typename F>
  Adapter (F const & f, RetT const & defVal, std::false_type const &,
           std::true_type const &)
   : Adapter{f, defVal, std::false_type{}, std::true_type{},
             std::is_same<void, decltype(f())>{}}
   { }

观察到,在所有情况下,我都构建了一个lambda函数,它接收 template <typename F> Adapter (F const & f, RetT const & defVal, std::true_type const &, std::false_type const &, std::true_type const &) : defV{defVal} { func = [&, this](Args && ... as) { f(std::forward<Args>(as)...); return defV; }; std::cout << "--- case 1 (full, void)" << std::endl; } template <typename F> Adapter (F const & f, RetT const &, std::true_type const &, std::false_type const &, std::false_type const &) { func = [&](Args && ... as) { return f(std::forward<Args>(as)...); }; std::cout << "--- case 2 (full, RetT)" << std::endl; } template <typename F> Adapter (F const & f, RetT const & defVal, std::false_type const &, std::true_type const &, std::true_type const &) : defV{defVal} { func = [&, this](Args && ...) { f(); return defV; }; std::cout << "--- case 3 (noArgs, void)" << std::endl; } template <typename F> Adapter (F const & f, RetT const &, std::false_type const &, std::true_type const &, std::false_type const &) { func = [&](Args && ...) { return f(); }; std::cout << "--- case 4 (noArgs, RetT)" << std::endl; } 个参数列表并仅在适当时使用它。

我理解这有点复杂,但以下是根据此解决方案修改的答案中的示例。

Args...