David Woo
David Woo

Reputation: 791

catch std::function allocations at compile time

I want to only allow use of std::function in my code base if it does not do any allocations.

To this end I can write something like the function below and only use it to create my function instances:

template< typename Functor>
std::function<Functor> makeFunction( Functor f)
{
    return std::function<Functor>(std::allocator_arg, DummyAllocator(), f);
}

where DummyAllocator will assert or throw if it ever gets used at runtime.

Ideally though I would like to catch allocating use cases at compile time.

i.e.

template< typename Functor>
std::function<Functor> makeFunction( Functor f)
{
   static_assert( size needed for function to wrap f < space available in function, 
   "error - function will need to allocate memory");

   return std::function<Functor>(f);
 }

Is something like this possible?

Upvotes: 7

Views: 766

Answers (3)

Niall
Niall

Reputation: 30614

The factory method you have is probably your best bet.

If not suitable, you may choose to implement an adaptor for function; implement the interface with the std::function as a member variable such that the adaptor enforces your constraints.

template <typename S>
class my_function {
  std::function<S> func_;
public:
  template <typename F>
  my_function(F&& f) :
  func_(std::allocator_arg, DummyAllocator(), std::forward<F>(f))
  {}
  // remaining functions required include operator()(...)
};

Upvotes: 2

user1095108
user1095108

Reputation: 14603

I'd write a std::function replacement that does not allocate, as std::function does allocate memory if it needs to, here's one candidate.

Upvotes: 2

Potatoswatter
Potatoswatter

Reputation: 137900

Given std::function allocator support in your library, simply provide std::function with an allocator that doesn't work.

template< typename t >
struct non_allocator : std::allocator< t > {
    t * allocate( std::size_t n ) { throw std::bad_alloc{}; }
    void deallocate( t * ) {}

    non_allocator() = default;
    template< typename u >
    non_allocator( non_allocator< u > const & ) {}

    template< typename u >
    struct rebind { typedef non_allocator< u > other; };
};

template< typename t, typename u >
bool operator == ( non_allocator< t > const &, non_allocator< t > const & )
    { return true; }

template< typename t, typename u >
bool operator != ( non_allocator< t > const &, non_allocator< t > const & )
    { return false; }

Unfortunately, this doesn't work in GCC because it does not even declare any allocator_arg constructors for function. Even in Clang, a compile-time error is impossible because it unfortunately uses a runtime if on a constant value to decide whether or not to use the allocator.

Upvotes: 0

Related Questions