constexpr version of ::std::function


I am in search of a ::std::function usable in constexpr. Use case: I have a function which takes a function pointer as an argument, and a second which passes a lambda to the first function. Both are fully executable at compile time, so I want to constexpr them. Eg:

template <class _Type> class ConstexprFunctionPtr { private: using Type = typename ::std::decay<_Type>::type; const Type function; public: constexpr inline ConstexprFunctionPtr(const Type f) : function(f) { } template <typename... Types> constexpr inline auto operator() (Types... args) const { return function(args... ); } }; constexpr inline void test() { ConstexprFunctionPtr<int(int)> test([](int i) -> int { return i + 1; }); int i = test(100); ConstexprFunctionPtr<int(int)> test2([=](int i) -> int { return i + 1; }); i = test2(1000); }

However, this only works because I am converting the lambda to a function pointer, and of course fails with capturing lambdas as showed in the second example. Can anyone give me some pointers on how to do that with capturing lambdas?

This would demonstrate the usecase:

constexpr void walkOverObjects(ObjectList d, ConstexprFunctionPtr<void(Object)> fun) { // for i in d, execute fun } constexpr void searchObjectX(ObjectList d) { walkOverObjects(d, /*lambda that searches X*/); }

Thanks, jack

Update: Thanks for pointing out the C++20 solution, however, I want one that works under C++14


I am in search of a ::std::function usable in constexpr


Stop right here. it's impossible. std::function is a polymorphic wrapper function. stateless lambdas, statefull lambdas, functors, function pointers, function references - all of them can build a valid std::function that can change during runtime. so making a compile time equivalent is just a waste of time.

If you just want a compile time generic function parameter, you can just use templates

template<class functor_type> class my_generic_function_consumer_class{ using decayed_function_type = typename std::decay_t<functor_type>; decayed_function_type m_functor; };

In your code in question, just accept a generic functor, and validate it using static_assert:

template<class function_type> constexpr void walkOverObjects(ObjectList d, function_type&& fun) { static_assert(std::is_constructible_v<std::function<void(ObjectList), function_type>>, "function_type given to walkOverObjects is invalid."); }

So a lot has changed with C++20 --- most importantly, you can now use dynamic memory and virtual functions within constexpr contexts. This makes it entirely possible to build a constexpr version of std::function. Here's a proof-of-concept (it's long and has no copy or move constructors, so please <em>do not use this as-is</em>). It compiles under clang 10, running code here. I've not tried it under other compilers, and it's worth noting that none of the major compilers claim to have a complete implementation of C++-20 at this time.

<pre class="lang-cpp prettyprint-override">#include <type_traits> #include <utility> #include <functional> template<typename Ret, typename... Args> struct _function{ constexpr virtual Ret operator()(Args...) const = 0; constexpr virtual ~_function() = default; }; template<typename F, typename Ret, typename... Args> struct _function_impl : public _function<Ret,Args...>{ F f; constexpr Ret operator()(Args... args) const override { return f(std::forward<Args>(args)...); } constexpr _function_impl(F&& f):f(f){} }; template<typename > struct function; template<typename Ret, typename... Args> struct function<Ret (Args...)>{ _function<Ret,Args...> *real_f{nullptr}; constexpr Ret operator()(Args... args) const { return real_f->operator()(std::forward<Args>(args)...); } constexpr ~function(){ if (real_f) delete real_f; } template<typename F> constexpr function(F&& f):real_f(new _function_impl<std::decay_t<F>,Ret,Args...>(std::move(f))){} }; template<typename Ret, typename... Args> constexpr Ret call_f_2(const function<Ret(Args...)> &f, Args... a){ return f(std::forward<Args>(a)...); } template<typename F, typename... Args> constexpr decltype(auto) call_f(F && f, Args&&... a){ using Ret = std::invoke_result_t<std::decay_t<F>,Args...>; function<Ret(Args...)> f2 = std::move(f); return call_f_2<Ret,Args...>(f2,a...); } int main(){ constexpr int c = 3; constexpr int i = call_f([c](int j) constexpr {return c + j;},4); return i; }