Document Number: N3574

Date: 2013-03-10

Project: Programming Language C++, Library Working Group

Reply-to: Mark Boyall

Binding stateful functions as function pointers


It's a common need for C++ programs to interoperate with C programs. This need is accepted and met in most cases. But there's one which remains to be addressed- function pointers. The limitations of function pointers surprise many a novice programmer, and frustrate even the experts. Some C APIs permit state to be passed in but the methods vary and, more importantly, the wrapping code has to be coded separately for each API, making it impossible to deal with in generic code. std::bound_function is intended to alleviate this issue.

Motivation and Scope

This proposal is motivated by the lack of a means to pass stateful functions to C APIs as a function pointer. Solving this problem would benefit everybody who has to call into a C API. The approach outlined here of a thunk, or trampoline, is well-understood and used commonly in language implementations which include a JIT compiler.

Impact on the Standard

The specification of std::bound_function is quite lightweight. However, there currently appears to be no way to generate a function pointer with extern "C" linkage, nor even any way to declare that type, as a member. This is problematic, as there is no way to use a template to produce such a type if it is not a member. A workaround was found for one major compiler where a template alias in the global namespace can be used. However, generally, the rules governing where extern "C" declarations may and may not take place may need to be revisited, as it appears to be impossible to define an extern "C" type which is templated on GCC and Visual Studio 2012, and it's uncertain if these implementations are deficient in this regard. However, Visual Studio 2012 does not implement the C language linkage for function pointers, so only GCC would need to change their language rules to support this. The workaround given in the specification functions on Clang 3.1.

Design decisions

The main intention here is to provide a simple design, as the objective is simple. The primary implementation hurdle will be the implementation of an allocator that can provide executable memory, whereas traditional allocators do not provide such. In addition, unlike std::function, a bound_function cannot be empty.

Technical specification

In header <functional>

extern "C" {
    template<typename Ret, typename... Args>
    using __extern_c_fp = Ret(*)(Args...);
namespace std {
    template<typename Signature> class bound_function;
    template<typename Ret, typename... Args> class bound_function<Ret(Args...)> {
        template<typename T> bound_function(T&& t) 
        noexcept(is_nothrow_constructible<typename std::decay<T>::type, T>::value);

It is required that the perfect forwarding constructor does not interfere with the copy or move constructors- that is, the decayed type of T cannot be bound_function.

        bound_function(const bound_function&) noexcept;
        bound_function(bound_function&&) noexcept;
        bound_function& operator=(const bound_function&) noexcept;
        bound_function& operator=(bound_function&&) noexcept;
        operator __extern_c_fp<Ret, Args...>() const noexcept;
        ~bound_function() noexcept;

When the bound_function is constructed, it shall prepare a function pointer that, when executed, shall invoke the function object passed to it's constructor, forwarding all arguments. The conversion operator shall provide this function pointer. The bound_function shares ownership of the function pointed to by the function pointer between copies. The function object's destructor shall not throw an exception, else, the behaviour is undefined.