C++ Conversion Operator for Converting to Function Pointer

C++ Conversion operator for converting to function pointer

Since you must know:

(*operator int() const)(int, int)
{
return _funcPtr;
}

(Fixed. Again.)


Update: I've been informed by Johannes Schraub and Luc Danton that this syntax is in fact not valid, and that you really must use a typedef. Since you say that typedefs aren't an option, here's a helper class that can wrap your typedef:

template<typename R, typename A1, typename A2>
struct MakeFunctionType
{
typedef R(*type)(A1, A2);
};

template<typename R, typename A1, typename A2>
operator typename MakeFunctionType<R, A1, A2>::type () const
{
// implementation is unimportant here
}

How to define a class type conversion to a pointer to function?

  • So why I cannot directly define conversion for my class using the type int(*)(int, int) but I can with a type alias?

The grammar for the "operator TYPE" name of a conversion function is much more restricted than a more general declarator or type-id. It doesn't allow parentheses at all, only a type specifier (like a type alias name, unsigned int, a class name, etc.), combinations of the *, &, &&, const and volatile tokens, and [[attributes]]. I can't say exactly why, but complicated declarations like that are tricky to write, read, and parse. Maybe there's a potential ambiguity in some case if more were allowed, or maybe they just didn't want to require compilers to have to figure this one out.

Also, if it were allowed, maybe the form would be operator int (*())(int, int); and not operator (int(*)(int, int))()? Or maybe that doesn't make sense either. See? Tricky.

  • Why I get value 1 in the first statement which is erroneous and get it correct in the second statement using parenthesis?

Function call syntax has higher precedence than C-style cast. So

(int(*)(int, int))Foo()(5, 7)    // (1)
(int(*)(int, int)) (Foo()(5, 7)) // (2), same as (1)
((int(*)(int, int))Foo()) (5, 7) // (3), not the same

Expression (1) or (2) evaluates by first creating a temporary Foo. It's followed by function call syntax, and Foo doesn't define an operator(), but C++ will also check if it implicitly converts to a pointer or reference to function and it does, so the (5, 7) does the implicit conversion and calls the resulting pointer to add, giving 12. This is cast to the function pointer type, which has implementation-defined results. There is no operator<< declared for function pointers, but there is one for bool, and a function pointer can implicitly convert to bool. Presumably the result of the strange cast was not a null pointer value, so the end result is true, and you see the value 1. (If you had done std::cout << std::boolalpha earlier, you should see true or an appropriate translation instead.)

One piece of this, besides the operator precedence misunderstanding, is the dangers of a C-style cast, which can do very many different things, some not usually intended. Use static_cast<int(*)(int,int)>(Foo())(5,7) instead, and everything's fine. Or if we accidentally typed static_cast<int(*)(int,int)>(Foo()(5,7)) instead, the compiler gives an error about converting from int to int(*)(int,int), since only reinterpret_cast or C-style cast may do that.

  • I get the warning from first statement: Description Resource Path Location Type cast to pointer from integer of different size [-Wint-to-pointer-cast] main.cpp /MyCppProj line 31 C/C++ Problem

Even though the C-style cast forces the conversion from int to function pointer to be valid, the compiler is warning that int doesn't have enough bytes to represent a function pointer. It's assuming the int value earlier came from casting a function pointer to some numeric type, and this is meant to convert back, but whenever it was converted from a type large enough to int, the pointer value was lost.

Templated conversion function to function pointer

Question #1: In C++03, is there was no way to specify a function conversion operator template? It appears there was no way to resolve the template arguments (i.e., name them in a deduced context) in an acceptable function pointer type.

Yes, that is correct.

Question #2: In C++11, can such a conversion be specified using a default template argument?

It can, and you can also use alias templates, but you cannot use such a conversion function template for creating surrogate call functions. You can use it for converting your class object to function pointers in implicit conversions otherwise.

We also have alias templates. It seems that alias substitution occurs before instantiation, given the example in §14.5.7/2, where the declarations of process conflict. In GCC 4.7, this code at least instantiates the declaration, but then it produces a bizarre "candidate expects 2 arguments, 2 provided" error.

Yes, this is https://groups.google.com/forum/?fromgroups#!topic/comp.std.c++/lXLFBcF_m3c (and caused closure of DR395), but even though such a conversion function template can work in cases like void(&p)() = yourClassObject, it won't work for surrogate call functions, because there the conversion function needs to provide a fixed non-dependent type that the class object is converted to when the surrogate function is called, but a conversion function template does not provide such a type normally (weird things like template<typename = int> operator Identity<void(*)()>(); aside...).

I think that GCC may incorrectly generates the candidate call-function(void (&)( t ), t) with the dependent types still there and try to call that candidate, thereby violating some invariant of it (which could explain the weird error message - possibly hitting an } else { ... } unexpectedly somewhere).

Converting a forwarding lambda to a function pointer

TL;DR: This is the specified behavior according to the standard. Template argument deduction has a special rule for deducing template arguments when taking the address of a function template, allowing forwarding references to work as expected. There is no such rule for conversion function templates.

Note: this looks like it's just an area for which nobody has written a proposal yet. If someone writes a proposal for this, it seems likely that this could be made to work in the future.


From [expr.prim.lambda]:

... . For a generic lambda with no lambda-capture, the closure type has a conversion function template to pointer to function. The conversion function template has the same invented template parameter list, and the pointer to function has the same parameter types, as the function call operator template. The return type of the pointer to function shall behave as if it were a decltype-specifier denoting the return type of the corresponding function call operator template specialization.

emphasis added

This states that the template arguments and function parameter types must be copied in a one-to-one manner:

// simplified version of the example in [expr.prim.lambda]/8
struct Closure {
template <typename T>
void operator()(T&& t) const {
/* ... */
}

template <typename T>
static void lambda_call_operator_invoker(T&& t) {
Closure()(std::forward<T>(t));
}

// Exactly copying the template parameter list and function parameter types.
template <typename T>
using fn_type = void(*)(T&&);
// using fn_type = void(*)(T); // this compiles, as noted later

template <typename T>
operator fn_type<T>() const {
return &lambda_call_operator_invoker<T>;
}
};

This fails to compile on all three of Clang, GCC, and MSVC, which can certainly be surprising, since we were expecting reference collapsing to happen on the T&& argument.

However, the standard doesn't support this.


The important parts of the standard are [temp.deduct.funcaddr] (deducing template arguments taking the address of a function template) and [temp.deduct.conv] (deducing conversion function template arguments). Critically, [temp.deduct.type] specifically mentions [temp.deduct.funcaddr], but not [temp.deduct.conv].

Some terms used in the standard:

  • P is the return type of the conversion template, or the type of the function template
  • A is the type we are "trying to convert to"

Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-type-list ([dcl.fct]) of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. If P and A are function types that originated from deduction when taking the address of a function template ([temp.deduct.funcaddr]) or when deducing template arguments from a function declaration ([temp.deduct.decl]) and Pi and Ai are parameters of the top-level parameter-type-list of P and A, respectively,
Pi is adjusted if it is a forwarding reference ([temp.deduct.call]) and Ai is an lvalue reference, in which case the type of Pi is changed to be the template parameter type (i.e., T&& is changed to simply T).

emphasis added

This specifically calls out taking the address of a function template, making forwarding references just work. There is no similar reference to conversion function templates.

Revisiting the example earlier, if we change fn_type to void(*)(T), that is the same operation that is described here in the standard.

Implicit cast from function to function pointer?

Yes, there's function-to-pointer implicit conversion:

An lvalue of function type T can be implicitly converted to a prvalue pointer to that function. This does not apply to non-static member functions because lvalues that refer to non-static member functions do not exist.

And

A pointer to function can be initialized with an address of a non-member function or a static member function. Because of the function-to-pointer implicit conversion, the address-of operator is optional:

void f(int);
void (*p1)(int) = &f;
void (*p2)(int) = f; // same as &f

That means when being used in context requiring a function pointer, function (except for non-static member function) would convert to function pointer implicitly, and the usage of operator& is optional.

Implement conversion operator for pointer

You can't. Conversion operators need to be members of a class, but foo* is not a user-defined class type, it's a pointer type (besides, int b = *a would work).

The best thing you can do is to use an utility function that does the casting.

Converting C to CPP with Function Pointers

Is this not possible in CPP?

Strictly speaking, no, due to type safety and other rules governing function prototypes. However, depending on your needs, that C code can be ported to C++.

Firstly, it should be noted that the function signature of void* fn(); in C is not the same in C++. In C++ to get the same function signature, you need to introduce variadic arguments like so: void* fn(...);, however, it should be noted that you cannot access the variadic arguments portably for a function signature like this.

In C++, void* fn(); is the same as void* fn(void); in C. To this, if your functions had variable inputs in C, you would need to do a little extra work in C++ using the variadic argument list.

For example, if your code were something similar to the following C code:

#include <stdio.h>
#define NUM_STEPS 3

static void* filterEvenFoos(void)
{
printf("42\n");
return NULL;
}

static void* timesFooByTwo(int val)
{
printf("%d\n", (val * 2));
return NULL;
}

static void* mapFooToBar(double obj1, size_t obj2)
{
printf("foo(%f)->bar(%zu)\n", obj1, obj2);
return NULL;
}

int main(void)
{
void* (*functionSteps[NUM_STEPS])();
functionSteps[0] = (void*)filterEvenFoos;
functionSteps[1] = (void*)timesFooByTwo;
functionSteps[2] = (void*)mapFooToBar;

functionSteps[0]();
functionSteps[1](42);
functionSteps[2](3.14, &main);
return 0;
}

You could port it to C++ in many ways, but you could use the va_arg functionality to get variable inputs like so:

#include <iostream>
#include <cstdarg>
#include <vector>

static void* filterEvenFoos(int na, ...)
{
std::cout << 42 << std::endl;
return NULL;
}

static void* timesFooByTwo(int na, ...)
{
va_list vl;
va_start(vl, na);
std::cout << ((va_arg(vl, int)) * 2) << std::endl;
va_end(vl);
return NULL;
}

static void* mapFooToBar(int na, ...)
{
va_list vl;
va_start(vl, na);
double obj1 = va_arg(vl, double);
size_t obj2 = va_arg(vl, size_t);
std::cout << "foo(" << obj1 << ")->bar(" << obj2 << ")" << std::endl;
va_end(vl);
return NULL;
}

int main(int argc, char* argv[])
{
std::vector<void* (*)(int, ...)> functionSteps;
functionSteps.push_back(&filterEvenFoos);
functionSteps.push_back(×FooByTwo);
functionSteps.push_back(&mapFooToBar);

functionSteps[0](0);
functionSteps[1](0, 42);
functionSteps[2](0, 3.14, &main);
return 0;
}

You might note that the function signature changes slightly to allow a portable way to access the variadic arguments in each function.

If you're using C++11, you could also make use of the std::function inside the vector, but you still need to have matching function signatures.

You could also make use of classes and inheritance, or template specializations, but those could be extreme over-kill in your scenario.

In the end, it's not a direct port from the C code to C++, but it is doable.

Hope that can help.

Why exactly can't function pointers be implicitly converted?

There are implicit and explicit conversions. A cast is always an explicit conversion and uses the (type) cast operator.

C has quite decent type safety when it comes to pointers. Apart from the special case of null pointer conversions, the only implicit pointer conversion allowed is between an object pointer and a pointer to void. Given that no type qualifiers are dropped. void* is not a generic type for function pointers though - no such type exists. As per C17 6.3.2.3/8, it's fine to convert between different function pointer types though, as long as the pointer is de-referenced with the correct type.

The specific rules for what's allowed and what isn't is found in C17 6.5.16.1 - Simple assignment. An assignment that fulfil the rules there would implicitly invoke the applicable conversion rule and it's a so-called "lvalue conversion" basically meaning that you can add type qualifiers to the left operand but you can't discard qualifiers present for the right operand.

Generally, 6.3.2.3 says what kind of conversions that can be done in general and 6.5.16.1 says what conversions that are allowed to be carried out implicitly during assignment.

To allow implicit conversions between various integer and floating point types is arguably a design flaw in the C language - it's a constant source of subtle and often severe bugs. Why C++ decided to fix it by enforcing explicit conversions everywhere. So the real question here is perhaps rather: why does C allow implicit conversions of some types?



Related Topics



Leave a reply



Submit