How could I pass std::function as function pointer?

前端 未结 3 1400
春和景丽
春和景丽 2021-01-15 09:23

I am trying to write a class template and internally it use a C function (implementation of BFGS optimization, provided by the R environment) with

3条回答
  •  北海茫月
    2021-01-15 09:52

    Let me say up front:

    I do not endorse the usage of the following library

    #include
    #include
    #include
    
    // func_traits
    template 
    struct func_traits : public func_traits::operator())> {};
    
    template 
    struct func_traits {
        using ptr_type = Ret (*) (Args...);
        using return_type =  Ret;
    
        template
        struct arg
        {
            using type = typename std::tuple_element>::type;
        };
    
        template
        using cast_return_type = Ret2 (*) (Args...);
    };
    
    template
    struct func_traits : public func_traits {};
    
    template 
    struct func_traits
    {
        using ptr_type = Ret (*) (Args...);
        using return_type =  Ret;
    
        template
        struct arg
        {
            using type = typename std::tuple_element>::type;
        };
    
        template
        using cast_return_type = Ret2 (*) (Args...);
    };
    
    
    
    // constexpr counter
    template 
    struct flag
    {
        friend constexpr int adl_flag(flag);
        constexpr operator int() { return N; }
    };
    
    template 
    struct write
    {
        friend constexpr int adl_flag(flag) { return N; }
        static constexpr int value = N;
    };
    
    template {})>
    constexpr int read(int, flag, int R = read(0, flag{}))
    {
        return R;
    }
    
    template 
    constexpr int read(float, flag)
    {
        return N;
    }
    
    template 
    constexpr int counter(int R = write{})>::value)
    {
        return R;
    }
    
    
    // fnptr
    template
    class fnptr
    {
        //these are to make sure fnptr is never constructed
        //technically the first one should be enough, but compilers are not entirely standard conformant
        explicit fnptr() = delete;
        fnptr(const fnptr&) {}
        ~fnptr() = delete;
    
        template
        static auto cast(Callable&& c, Ret(*fp)(Args...)) -> decltype(fp)
        {
            using callable_type = std::remove_reference_t;
            static callable_type clb{std::forward(c)};
            static bool full = false;
            if(full)
            {
                clb.~callable_type();
                new (&clb) decltype(clb){std::forward(c)};
            }
            else
                full = true;
            return [](Args... args) noexcept(noexcept(clb(std::forward(args)...))) -> Ret
            {
                return Ret(clb(std::forward(args)...));
            };
        }
    
    public:
        template
        static Signature* cast(Callable&& c)
        {
            return cast(std::forward(c), static_cast(nullptr));
        }
    
        template
        static auto cast(Ret (*fp)(Args...))
        {
            static decltype(fp) fnptr;
            fnptr = fp;
            using return_type = typename func_traits::return_type;
            return [](Args... args) noexcept(noexcept(fp(std::forward(args)...)) -> return_type
            {
                return return_type(fnptr(std::forward(args)...));
            };
        }
    
        template
        static auto get(Callable&& c)
        {
            return cast(std::forward(c), typename func_traits::ptr_type{nullptr});
        }
    
        template
        static auto get(Ret (*fp)(Args...))
        {
            return fp;
        }
    };
    

    And use it as

    #include
    #include
    
    using optimfn = double (int, double*, void*);
    using optimgr = void (int, double*, double*, void*);
    
    void test(optimfn* fn, optimgr* gr)
    {
        double d;
        fn(42, &d, &d);
        gr(42, &d, &d, &d);
    }
    
    int main()
    {
        std::function fn = [](int, double*, void*){
            std::cout << "I'm fn" << std::endl;
            return 0.;
        };
        std::function gr = [](int, double*, double*, void*){
            std::cout << "I'm gr" << std::endl;
        };
    
        test(fnptr<>::get(fn), fnptr<>::get(gr));
    }
    

    Live example

    func_traits

    Is just a helper traits type that will fetch the type of any callable in an easily accessible form

    constexpr counter

    This is half the evilness of what's going on. For details visit is stateful metaprogramming ill formed yet?

    fnptr

    The actual meat of the code. It takes any callable with appropriate signatures and implicitly declares an anonymous C function at every point it is called and coerces the callable into the C function.

    It has the funky syntax fnptr<>::get and fnptr<>::cast. This is intentional.

    get will declare the anonymous C function with the same signature as the callable object.

    cast works on any compatible callable type, that is, if the return type and arguments are implicitly convertible, it can be casted.

    Caveats

    fnptr implicitly declares an anonymous C function at each point in the code it is called. It is not the same as std::function that is actually a variable.

    If you call the same fnptr in the code again, all hell breaks lose.

    std::vector v;
    for(int i = 0; i < 10; i++)
        v.push_back(fnptr<>::get([i]{return i;}));  // This will implode
    

    You have been warned.

提交回复
热议问题