Is It Illegal to Invoke a Std::Function<Void(Args...)> Under the Standard

Is it illegal to invoke a std::functionvoid(Args...) under the standard?

Yes, your analysis is correct; I came to the same conclusion here.

According to Daniel Kruegler, this issue should appear on the library defect list subsequent to the next mailing:

A corresponding library issue has already been submitted, but is not
yet visible in the issue list.

Hopefully once that becomes visible we'll also have a conclusive answer to whether it is allowable to construct a std::function with signature returning void passing a callable with signature returning non-void (Using `std::function<void(...)>` to call non-void function).


Update: this was entered as LWG 2420, which was resolved in favor of special-casing void return type to static_cast the result of the invoked function to void. This means that a callable returning non-void can be the target of a std::function<void(...)>. LWG2420 was applied as a post-publication correction to C++14; meanwhile, all compilers I'm aware of effectively apply this behavior as an extension in C++11 mode.

Using `std::functionvoid(...)` to call non-void function

Your code has undefined behavior. It may or may not work as you expect. The reason it has undefined behavior is because of 20.8.11.2.1 [func.wrap.func.con]/p7:

Requires: F shall be CopyConstructible. f shall be Callable (20.8.11.2) for argument types ArgTypes and return type R.

For f to be Callable for return type R, f must return something implicitly convertible to the return type of the std::function (void in your case). And int is not implicitly convertible to void.

I would expect your code to work on most implementations. However on at least one implementation (libc++), it fails to compile:

test.cpp:7:30: error: no viable conversion from 'int (int)' to 'std::function<void (int)>'
std::function<void(int)> ff = f;
^ ~

Ironically the rationale for this behavior stems from another SO question.

The other question presented a problem with std::function usage. The solution to that problem involved having the implementation enforce the Requires: clause at compile time. In contrast, the solution to this question's problem is forbidding the implementation from enforcing the Requires: clause.

Isn't the template argument (the signature) of std::function part of its type?

The problem is that both function<int()> and function<int(int)> are constructible from the same function. This is what the constructor declaration of std::function looks like in VS2010:

template<class _Fx>
function(_Fx _Func, typename _Not_integral<!_Is_integral<_Fx>::value, int>::_Type = 0);

Ignoring the SFINAE part, it is constructible from pretty much anything.

std::/boost::function employ a technique called type erasure, to allow arbitary objects/functions to be passed in, so long they satisfy the signature when being called. One drawback from that is, that you get an error in the deepest part of the implementation (where the saved function is being called) when supplying an object which can't be called like the signature wants it to, instead of in the constructor.


The problem can be illustrated with this little class:

template<class Signature>
class myfunc{
public:
template<class Func>
myfunc(Func a_func){
// ...
}
};

Now, when the compiler searches for valid functions for the overload set, it tries to convert the arguments if no perfect fitting function exists. The conversion can happen through the constructor of the parameter of the function, or through a conversion operator of the argument given to the function. In our case, it's the former.

The compiler tries the first overload of a. To make it viable, it needs to make a conversion. To convert a int(*)() to a myfunc<int()>, it tries the constructor of myfunc. Being a template that takes anything, the conversion naturally succeeds.

Now it tries the same with the second overload. The constructor still being the same and still taking anything given to it, the conversion works too.

Being left with 2 functions in the overload set, the compiler is a sad panda and doesn't know what to do, so it simply says the call is ambigious.


So in the end, the Signature part of the template does belong to the type when making declarations/definitions, but doesn't when you want to construct an object.


Edit:

With all my attention on answering the title-question, I totally forgot about your second question. :(

Can I circumvent it or will I have to keep the (annoying) explicit casts?

Afaik, you have 3 options.

  • Keep the cast
  • Make a function object of the appropriate type and pass that

    function<int()> fx = x;
    function<int(int)> fy = y;
    a(fx);
    a(fy);

  • Hide the tedious casting in a function and use TMP to get the right signature

The TMP (template metaprogramming) version is quite verbose and with boilerplate code, but it hides the casting from the client. An example version can be found here, which relies on the get_signature metafunction that is partially specialized on function pointer types (and provides a nice example how pattern matching can work in C++):

template<class F>
struct get_signature;

template<class R>
struct get_signature<R(*)()>{
typedef R type();
};

template<class R, class A1>
struct get_signature<R(*)(A1)>{
typedef R type(A1);
};

Of course, this needs to be extended for the number of arguments you want to support, but that is done once and then buried in a "get_signature.h" header. :)

Another option I consider but immediatly discarded was SFINAE, which would introduce even more boilerplate code than the TMP version.

So, yeah, that are the options that I know of. Hope one of them works for you. :)

Should I use std::function or a function pointer in C++?

In short, use std::function unless you have a reason not to.

Function pointers have the disadvantage of not being able to capture some context. You won't be able to for example pass a lambda function as a callback which captures some context variables (but it will work if it doesn't capture any). Calling a member variable of an object (i.e. non-static) is thus also not possible, since the object (this-pointer) needs to be captured.(1)

std::function (since C++11) is primarily to store a function (passing it around doesn't require it to be stored). Hence if you want to store the callback for example in a member variable, it's probably your best choice. But also if you don't store it, it's a good "first choice" although it has the disadvantage of introducing some (very small) overhead when being called (so in a very performance-critical situation it might be a problem but in most it should not). It is very "universal": if you care a lot about consistent and readable code as well as don't want to think about every choice you make (i.e. want to keep it simple), use std::function for every function you pass around.

Think about a third option: If you're about to implement a small function which then reports something via the provided callback function, consider a template parameter, which can then be any callable object, i.e. a function pointer, a functor, a lambda, a std::function, ... Drawback here is that your (outer) function becomes a template and hence needs to be implemented in the header. On the other hand you get the advantage that the call to the callback can be inlined, as the client code of your (outer) function "sees" the call to the callback will the exact type information being available.

Example for the version with the template parameter (write & instead of && for pre-C++11):

template <typename CallbackFunction>
void myFunction(..., CallbackFunction && callback) {
...
callback(...);
...
}

As you can see in the following table, all of them have their advantages and disadvantages:























































function ptrstd::functiontemplate param
can capture context variablesno1yesyes
no call overhead (see comments)yesnoyes
can be inlined (see comments)nonoyes
can be stored in a class memberyesyesno2
can be implemented outside of headeryesyesno
supported without C++11 standardyesno3yes
nicely readable (my opinion)noyes(yes)

Where is it prohibited for target object of std::function to throw on destruction?

It's a library-wide requirement, specified in [res.on.functions]:

In certain cases ([...], operations on types used to instantiate standard
library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, this document places no requirements on the implementation.

In particular, the effects are undefined in the following cases:

  • [...]
  • If any [...] destructor operation exits via an exception, unless
    specifically allowed in the applicable Required behavior: paragraph.

Wrapping around a C function that takes its arguments with void* in C++

In C++11 onwards, you can use something like

static void call_task(void *args) {
auto& f = *static_cast<std::function<void()>*>(args);
f();
}
// note: need this to stay alive!
std::function<void()> f = [&](){
// Any arguments you like here
do_whatever(1, 2, 3)
};
CreateTask(call_task, static_cast<void*>(&f));

You need to ensure the lifetime of f is longer than that of the task (just as you would for your Params object).


You can actually avoid std::function altogether, as:

template<typename Func>
void call_func(void *args) {
auto& f = *static_cast<Func*>(args);
f();
}

template<typename Func>
void wrapped_create_task(Func& func) {
CreateTask(call_func<Func>, static_cast<void*>(&func));
}
// you can still use `std::function` here, but you don't have to.
auto f = [&](){
// Any arguments you like here
do_whatever(1, 2, 3)
};

// this works on any object that implements `operator ()`
wrapped_create_task(f)

Again, it's really important that f remains alive for the duration of its execution. You can't put it on a stack that dies before the task does.

Deleting a std::function object within itself

This program has well-defined behavior and demonstrates a g++ bug.

The only questionable part of runtime is during the statement (*f)();. The behavior of that line can be picked apart piece by piece. The Standard section numbers below are from N3485; apologies if some don't match C++11.

*f is just the built-in unary operator on a raw pointer to class type. No problem here. The only other evaluation is the function-call expression (*f)(), which invokes void std::function<void()>::operator() const. Then that full-expression is a discarded value.

20.8.11.2.4:

R operator()(ArgTypes... args) const

Effects: INVOKE(obj, std::forward<ArgTypes>(args)..., R) where obj is the target object of *this.

(I've replaced "f" in the Standard with "obj" to reduce confusion with main's f.)

Here obj is a copy of the lambda object, ArgTypes is the empty parameter pack from the specialization std::function<void()>, and R is void.

The INVOKE pseudo-macro is defined in 20.8.2. Since the type of obj is not a pointer-to-member, INVOKE(obj, void) is defined to be obj() implicitly converted to void.

5.1.2p5:

The closure type for a lambda-expression has a public inline function call operator ...

... with exactly described declaration. In this case it turns out to be void operator() const. And its definition is exactly described too:

5.1.2p7:

The lambda-expression's compound-statement yields the function-body of the function call operator, but for purposes of name lookup, determining the type and value of this and transforming id-expressions referring to non-static class members into class member access expressions using (*this), the compound-statement is considered in the context of the lambda-expression.

5.1.2p14:

For each entity captured by copy, an unnamed non-static data member is declared in the closure type.

5.1.2p17:

Every id-expression that is an odr-use of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.

So the lambda function call operator must be equivalent to:

void __lambda_type::operator() const {
delete __unnamed_member_f;
}

(where I've invented some names for the unnamed lambda type and unnamed data member.)

The single statement of that call operator is of course equivalent to delete (*this).__unnamed_member_f; So we have:

  • The built-in unary operator* dereference (on the prvalue this)
  • A member access expression
  • A value computation (aka lvalue-to-rvalue conversion) for the member subobject
  • A scalar delete expression
    • Invokes std::function<void()>::~function()
    • Invokes void operator delete(void*)

And finally, in 5.3.5p4:

The cast-expression in a delete-expression shall be evaluated exactly once.

(Here is where g++ is wrong, doing a second value computation on the member subobject between the destructor call and the deallocation function.)

This code cannot cause any other value computations or side effects after the delete expression.

There are some allowances for implementation-defined behavior in lambda types and lambda objects, but none that affect anything above:

5.1.2p3:

An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:

  • the size and/or alignment of the closure type,

  • whether the closure type is trivially copyable,

  • whether the closure type is a standard-layout class, or

  • whether the closure type is a POD class.

Should std::function assignment ignore return type?

The code is undefined behavior in C++11, and ill-formed in C++14. C++14 adds this Remark to the specification of this constructor:

Remarks: These constructors shall not participate in overload
resolution unless f is Callable (20.9.11.2) for argument types
ArgTypes... and return type R.

Callable is defined in [func.wrap.func]/p2:

A callable object f of type F is Callable for argument types ArgTypes
and return type R if the expression INVOKE (f, declval<ArgTypes>()..., R), considered as an unevaluated operand
(Clause 5), is well formed (20.9.2).

For this INVOKE to be well formed, the return type of INVOKE without the R must be implicitly convertible to R ([func.require]/p2).

In C++11 these statements were under a Requries clause, which means it is up to the client to get them right, and if the client fails, anything can happen, including successful compilation.

This was changed by LWG 2132.

What are C++ functors and their uses?

A functor is pretty much just a class which defines the operator(). That lets you create objects which "look like" a function:

// this is a functor
struct add_x {
add_x(int val) : x(val) {} // Constructor
int operator()(int y) const { return x + y; }

private:
int x;
};

// Now you can use it like this:
add_x add42(42); // create an instance of the functor class
int i = add42(8); // and "call" it
assert(i == 50); // and it added 42 to its argument

std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out(in.size());
// Pass a functor to std::transform, which calls the functor on every element
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1));
assert(out[i] == in[i] + 1); // for all i

There are a couple of nice things about functors. One is that unlike regular functions, they can contain state. The above example creates a function which adds 42 to whatever you give it. But that value 42 is not hardcoded, it was specified as a constructor argument when we created our functor instance. I could create another adder, which added 27, just by calling the constructor with a different value. This makes them nicely customizable.

As the last lines show, you often pass functors as arguments to other functions such as std::transform or the other standard library algorithms. You could do the same with a regular function pointer except, as I said above, functors can be "customized" because they contain state, making them more flexible (If I wanted to use a function pointer, I'd have to write a function which added exactly 1 to its argument. The functor is general, and adds whatever you initialized it with), and they are also potentially more efficient. In the above example, the compiler knows exactly which function std::transform should call. It should call add_x::operator(). That means it can inline that function call. And that makes it just as efficient as if I had manually called the function on each value of the vector.

If I had passed a function pointer instead, the compiler couldn't immediately see which function it points to, so unless it performs some fairly complex global optimizations, it'd have to dereference the pointer at runtime, and then make the call.



Related Topics



Leave a reply



Submit