代码之家  ›  专栏  ›  技术社区  ›  Panakotta00

如何组合lambda、vairadic函数和函数指针?

  •  4
  • Panakotta00  · 技术社区  · 8 年前

    我有一个好主意,它(对我)简化了很多事情。

    假设您有一个函数,它接受一个带有x个参数的函数,如果发生事件,该函数将被执行。为了简化这个过程,您使用typedef定义了一个新类型,即函数指针。

    typedef void (*HandlerFunction)(...);
    
    void setHandler(HandlerFunction fun) {...}
    

    现在您可以使用setHandler函数。。。使用带有附加sepcific参数的lambda函数设置处理程序,因为您知道此函数将始终使用这些特定参数调用。

    setHandler([](int i, std::string arg) {
        std::cout << "Event with i=" << i << " and arg=" << arg << "!" << std::endl;
    });
    

    现在的问题是。。。在C++中,这是可能的吗?

    4 回复  |  直到 8 年前
        1
  •  2
  •   Korni    8 年前

    可变参数

    我弄明白了,如何使用可变参数列表。诀窍是在可变函数周围使用包装器并使用 va_copy :

    #include <iostream>
    #include <functional>
    #include <cstdarg>
    
    std::function<void (int, va_list)> foo = [] (int numargs, va_list args)
    {
         va_list args_copy;
         va_copy(args_copy, args);
         std::cout << "Old Foo " <<va_arg(args_copy,int) << std::endl;
         va_end(args_copy);
    };
    
    void set_foo(std::function<void (int, va_list)> new_foo)
    {
        foo = new_foo;
    }
    
    void wrapper(int numargs, ...)
    {
        va_list args;
        va_start(args, numargs);
        foo(numargs, args);
        va_end(args);
    }
    
    int main() {
    
        wrapper(123,456);
    
        set_foo([](int numargs, va_list args)
        {
             va_list args_copy;
             va_copy(args_copy, args);
             std::cout << "New Foo " << va_arg(args_copy,int) << std::endl;
             va_end(args_copy);
         });
    
        wrapper(123,456);
        return 0;
    }
    

    这导致:

    Old Foo 456
    New Foo 456
    

    我不知道为什么需要包装纸,也许有人知道。但它是有效的:P


    已知论点

    你可以使用 initializer_list 将特定类型的变量参数传递给变量lambda。

    #include <iostream>
    #include <functional>
    #include <initializer_list>
    
    std::function<void (std::initializer_list<int>)> foo = [](std::initializer_list<int> args) 
      {
        for (auto i: args) std::cout << "Old Foo: "<< i << '\n';
      };
    
    void setFoo(std::function<void (std::initializer_list<int>)> new_foo) {
        foo = new_foo;
    };
    
    
    int main()
    {
      foo({2, 4, 5});
      std::cout << std::endl;
      setFoo([](std::initializer_list<int> args) 
      {
        for (auto i: args) std::cout << "New Foo: "<< i << '\n';
      });
      foo({2, 4});
    }
    

    这将返回:

    Old Foo: 2
    Old Foo: 4
    Old Foo: 5
    
    New Foo: 2
    New Foo: 4
    
        2
  •  0
  •   Panakotta00    8 年前

    在我看来,我已经找到了最好的解决办法。

    我将此类用作函数的容器:

    #define C(...) __VA_ARGS__
    #define F(a, ...) (std::function<void(__VA_ARGS__)>)[ a ](__VA_ARGS__)
    
    #pragma once
    
    #include <memory>
    #include <functional>
    
    class HandlerFunc {
    private:
        struct function_b {
            virtual ~function_b() {};
        };
    
        template<typename... Args>
        struct func : function_b {
            typedef std::function<void(Args...)> func_t;
            func_t funct;
            func(func_t f) : funct(f) {};
        };
    
        std::unique_ptr<function_b> fb;
    public:
        template <typename... Args>
        HandlerFunc(std::function<void(Args...)> f) : fb(new func<Args...>(f)) { };
    
        template <typename... Args>
        HandlerFunc& operator=(std::function<void(Args...)> f) {
            fb = std::unique_ptr<function_b>(new func<Args...>(f));
            return *this;
        };
    
        template <typename... Args>
        void operator()(Args... args) const {
            func<Args...>* f = dynamic_cast<func<Args...>*>(fb.get());
            if (f)
                f->funct(args...);
            else
                throw std::runtime_error("Invalid arguments to function object call!");
        };
    };
    

    现在可以编写如下代码:

    std::vector<HandlerFunc> funcs;
    
    template<typename... Args>
    void add(std::function<void(Args...)> func) {
        funcs.push_back(func);
    }
    
    int main() {
        int a = 321;
        int b = 123;
    
        add(F(C(a, b), int i){
            std::cout << a << b << i << std::endl;
        });
        add(F(C(=), std::string str){
            std::cout << b << a << str << std::endl;
        });
    
        int i = 0;
        for (auto& func : funcs) {
            if (i < 1) func(1234);
            else func("meep");
        }
    }
    

    add(F([=], int i, std::string str){
        ...
    });
    

    进入这个

    add((std::function<void(int, std::string))[=](int i, std::string str){
        ...
    });
    

    我对预处理器之类的东西很陌生^^

        3
  •  -1
  •   hegel5000    8 年前

    我在想象你 希望 要执行如下操作:

    Delegator delegator;
    delegator.set_handler(handler);
    

    不幸的是 handler 或者它的类型签名(带有 std::function )必须知道什么时候 Delegator

    //The library user defines this
    struct MyHandlers {
      void operator()(int){ 
        std::cout << "Event with i=" << i << std::endl;
      }
    
      template<class T>
      void operator()(std::string str, T t_){  
        std::cout << "Event with str=" << str << " and some class=" << t_ << "!" << std::endl;
      }
    
      void operator()(std::string str){ /** more code **/ }
    };
    

    然后你可以做一些类似于你最初想做的事情。

    //This would go in the library
    template<class Handlers>
    struct Delegator_t {
      Handlers handlers;
    
      template<class T>
      void do_stuff_which_eventually_calls_handlers(T t_) {
        std::string string = "string";
        if (/** condition **/) {
          handlers(5);
        } else {
          handlers(string, t_);
        }
        //The user's third overload never gets called.
      }
    }
    template<class Handlers>
    Delegator_t<Handlers> Delegator(Handlers handlers){ return{std::move(handlers)}; }
    

    最后,用户可以使用您的库及其 MyHandlers 使用以下命令初始化

    auto delegator = Delegator(MyHandlers{});
    SomeClass some_class(/** some args **/);
    delegator.do_stuff_which_eventually_calls_handlers(some_class);
    

    当然,如果用户必须在 到达目的地的时间点 相同的

        4
  •  -1
  •   sardok    8 年前

    你可以做的一些事情

    #include <iostream>
    #include <functional>
    
    void exec_handler(std::function<void(int, std::string)> cb) {
            static int cnt = 0;
            cb(cnt++, "from_exec_handler");
    }
    
    void handler1(int count, std::string name) {
            std::cout << "handler1\t" << count << " " << name << std::endl;
    }
    
    void handler2(int count, std::string name, std::string name2) {
            std::cout << "handler2\t" << count << " " << name << " " << name2 << std::endl;
    }
    
    
    int main(int argc, char *argv[]) {
            auto handler_lambda = [](int count, std::string name) {
                            std::cout << "lambda\t\t" << count << " " << name << std::endl;
                            };
            auto clojure = [](int count, std::string name) {
                    return handler2(count, name, "clojure");
            };
            auto partial = std::bind(handler2, std::placeholders::_1, std::placeholders::_2, "partial");
            exec_handler(handler1);
            exec_handler(handler_lambda);
            exec_handler(clojure);
            exec_handler(partial);
    }
    

    输出:

    handler1        0 from_exec_handler
    lambda          1 from_exec_handler
    handler2        2 from_exec_handler clojure
    handler2        3 from_exec_handler partial