Skip to content

Complete implementation of std::function, std::function_ref, and std::move_only_function

License

Notifications You must be signed in to change notification settings

zhihaoy/nontype_functional

Repository files navigation

nontype<functional>

GitHub tag GitHub license CMake

Provide complete implementation of std::function, std::function_ref, and std::move_only_function equivalent to those in the C++23 <functional> header.

Highlights

  • Macro-free implementation
  • The size of each specialization is two pointers
  • Not require RTTI
  • Support classes without operator()

The implementation does not guarantee the best performance under all use cases but provides adequate code size & quality while maintaining full conformance.1

Supported toolchains

Toolset Standard Library Test Environment
GCC >= 11.1.0 libstdc++ Ubuntu 20.04
MSVC >= 14.30 Microsoft STL Visual Studio 2022

Installation

It's a header-only library. You may also install and consume its CMake targets:

find_package(nontype_functional CONFIG REQUIRED)
target_link_libraries("main" PRIVATE std23::nontype_functional)

Getting started

#include <std23/function_ref.h>

using std23::function_ref;

void parse_ini(function_ref<size_t(char *, size_t)> read_cb);

...

#include <stdio.h>

int main()
{
    auto fp = ::fopen("my.ini", "r");
    parse_ini([fp](auto ptr, auto n)
              { return ::fread(ptr, 1, n, fp); });
    ::fclose(fp);
}

Ignore the fact that the code has no error handling or resource-safety; the callable wrappers, function_ref in the example, generalized the idea of callbacks. You can pass anything with a matching call pattern to another function, parse_ini in here, without turning the latter into a template.

Now, what if you have an existing class that can read data, but it's not a function object?

class data_source
{
    ...

  public:
    auto read(char *, size_t) -> size_t;
};

Then you may designate a named member function, read in this example, to serve the role of an operator():

using std23::nontype;

int main()
{
    data_source input;
    parse_ini({nontype<&data_source::read>, input});
}

The nontype tag generalized the idea of delegates from other languages, like C♯. What replaces operator() doesn't have to be a member function. You can also use a free function or even a lambda:

int main()
{
    auto fp = ::fopen("my.ini", "r");
    parse_ini({nontype<[](FILE *fh, auto ptr, auto n)
                       { return ::fread(ptr, 1, n, fh); }>,
               fp});
    ::fclose(fp);
}

Feels like creating a member function for FILE on the fly, isn't it?

Roadmap

  • 0.8 – std::function_ref & std::function
  • 0.9 – std::move_only_function
  • 1.0 – nontype_t constructors for move_only_function
  • 1.1 – copyable_function from P2548
  • 1.2 – Support C++20 modules

See also

cppreference page for std::function
cppreference page for std::move_only_function
std::function_ref specification

Footnotes

  1. Except for std::function's target() member function, which is unimplemented because it requires RTTI.