Are two function pointers that are not exactly the same ever compatible? - c++

I have these function pointer types:
typedef int(*a)(char*);
typedef const int(*b)(char*);
typedef int(*c)(char* const);
typedef int(*d)(const char*);
typedef long(*e)(char*);
typedef int(*f)(unsigned char*);
typedef void(*g)(char*);
I know that a and c are exactly the same (at least in C++), as the const is ignored in function prototype argument types.
My question is if I have a variable of type a, and another variable of any of those 7 types, which of them would I be able to assign to the first variable?
a foo = NULL;
(a/b/c/d/e/f/g) bar = ...;
foo = bar; // Is this UB based on the type of bar?
Would I be able to detect it?
I tried taking advantage of how template<class F> ::std::function::operator=(F&&) is defined, as "This operator does not participate in overload resolution unless f is Callable for argument types Args... and return type R."
#include <iostream>
#include <functional>
#include <type_traits>
template<class T, class U>
static void _print_is_assignable(const char* const t_name, const char* const u_name) {
using function_t = ::std::function<typename ::std::remove_pointer<T>::type>;
std::cout << t_name;
std::cout << (::std::is_assignable<function_t&, U>::value ? " == " : " != ");
std::cout << u_name << '\n';
}
#define PRINT_IS_ASSIGNABLE(T, U) _print_is_assignable<T, U>(#T, #U)
typedef int(*a)(char*);
typedef const int(*b)(char*);
typedef int(*c)(char* const);
typedef int(*d)(const char*);
typedef long(*e)(char*);
typedef int(*f)(unsigned char*);
typedef void(*g)(char*);
int main() {
PRINT_IS_ASSIGNABLE(a, a); // a == a
PRINT_IS_ASSIGNABLE(a, b); // a == b
PRINT_IS_ASSIGNABLE(a, c); // a == c
PRINT_IS_ASSIGNABLE(a, d); // a == d
PRINT_IS_ASSIGNABLE(a, e); // a == e
PRINT_IS_ASSIGNABLE(a, f); // a != f
PRINT_IS_ASSIGNABLE(a, g); // a != g
PRINT_IS_ASSIGNABLE(g, a); // g == a
}

I've read the spec a bit, and the closest thing I could find is in section 5.2.10 [expr.reinterpret.cast]:
A function pointer can be explicitly converted to a function pointer of a different type. The effect of calling a function through a pointer to a function type (8.3.5) that is not the same as the type used in the definition of the function is undefined. Except that converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are function types) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.
Which seems to tell me that, no, it is undefined behaviour, unless the function pointer types are exactly the same, and I would have to cast back to the original type.
With some testing, g++ doesn't seem to mind if pointers are returned what pointers are returned, and if the return type is const qualified doesn't matter, which makes sense. I would just have to use std::function when trying to deal with almost compatible types.

Related

Is there something like `std::tie` for passing values from returned struct into existing variables? [duplicate]

Consider I have a custom type (which I can extend):
struct Foo {
int a;
string b;
};
How can I make an instance of this object assignable to a std::tie, i.e. std::tuple of references?
Foo foo = ...;
int a;
string b;
std::tie(a, b) = foo;
Failed attempts:
Overloading the assignment operator for tuple<int&,string&> = Foo is not possible, since assignment operator is one of the binary operators which have to be members of the left hand side object.
So I tried to solve this by implementing a suitable tuple-conversion operator. The following versions fail:
operator tuple<int,string>() const
operator tuple<const int&,const string&>() const
They result in an error at the assignment, telling that "operator = is not overloaded for tuple<int&,string&> = Foo". I guess this is because "conversion to any type X + deducing template parameter X for operator=" don't work together, only one of them at once.
Imperfect attempt:
Hence I tried to implement a conversion operator for the exact type of the tie:
operator tuple<int&,string&>() const Demo
operator tuple<int&,string&>() Demo
The assignment now works since types are now (after conversion) exactly the same, but this won't work for three scenarios which I'd like to support:
If the tie has variables of different but convertible types bound (i.e. change int a; to long long a; on the client side), it fails since the types have to fully match. This contradicts the usual use of assigning a tuple to a tuple of references which allows convertible types.(1)
The conversion operator needs to return a tie which has to be given lvalue references. This won't work for temporary values or const members.(2)
If the conversion operator is not const, the assignment also fails for a const Foo on the right hand side. To implement a const version of the conversion, we need to hack away const-ness of the members of the const subject. This is ugly and might be abused, resulting in undefined behavior.
I only see an alternative in providing my own tie function + class together with my "tie-able" objects, which makes me force to duplicate the functionality of std::tie which I don't like (not that I find it difficult to do so, but it feels wrong to have to do it).
I think at the end of the day, the conclusion is that this is one drawback of a library-only tuple implementation. They're not as magic as we'd like them to be.
EDIT:
As it turns out, there doesn't seem to be a real solution addressing all of the above problems. A very good answer would explain why this isn't solvable. In particular, I'd like someone to shed some light on why the "failed attempts" can't possibly work.
(1): A horrible hack is to write the conversion as a template and convert to the requested member types in the conversion operator. It's a horrible hack because I don't know where to store these converted members. In this demo I use static variables, but this is not thread-reentrant.
(2): Same hack as in (1) can be applied.
Why the current attempts fail
std::tie(a, b) produces a std::tuple<int&, string&>.
This type is not related to std::tuple<int, string> etc.
std::tuple<T...>s have several assignment-operators:
A default assignment-operator, that takes a std::tuple<T...>
A tuple-converting assignment-operator template with a type parameter pack U..., that takes a std::tuple<U...>
A pair-converting assignment-operator template with two type parameters U1, U2, that takes a std::pair<U1, U2>
For those three versions exist copy- and move-variants; add either a const& or a && to the types they take.
The assignment-operator templates have to deduce their template arguments from the function argument type (i.e. of the type of the RHS of the assignment-expression).
Without a conversion operator in Foo, none of those assignment-operators are viable for std::tie(a,b) = foo.
If you add a conversion operator to Foo,
then only the default assignment-operator becomes viable:
Template type deduction does not take user-defined conversions into account.
That is, you cannot deduce template arguments for the assignment-operator templates from the type Foo.
Since only one user-defined conversion is allowed in an implicit conversion sequence, the type the conversion operator converts to must match the type of the default assignment operator exactly. That is, it must use the exact same tuple element types as the result of std::tie.
To support conversions of the element types (e.g. assignment of Foo::a to a long), the conversion operator of Foo has to be a template:
struct Foo {
int a;
string b;
template<typename T, typename U>
operator std::tuple<T, U>();
};
However, the element types of std::tie are references.
Since you should not return a reference to a temporary,
the options for conversions inside the operator template are quite limited
(heap, type punning, static, thread local, etc).
There are only two ways you can try to go:
Use the templated assignment-operators:
You need to publicly derive from a type the templated assignment-operator matches exactly.
Use the non-templated assignment-operators:
Offer a non-explicit conversion to the type the non-templated copy-operator expects, so it will be used.
There is no third option.
In both cases, your type must contain the elements you want to assign, no way around it.
#include <iostream>
#include <tuple>
using namespace std;
struct X : tuple<int,int> {
};
struct Y {
int i;
operator tuple<int&,int&>() {return tuple<int&,int&>{i,i};}
};
int main()
{
int a, b;
tie(a, b) = make_tuple(9,9);
tie(a, b) = X{};
tie(a, b) = Y{};
cout << a << ' ' << b << '\n';
}
On coliru: http://coliru.stacked-crooked.com/a/315d4a43c62eec8d
As the other answers already explain, you have to either inherit from a tuple (in order to match the assignment operator template) or convert to the exact same tuple of references (in order to match the non-templated assignment operator taking a tuple of references of the same types).
If you'd inherit from a tuple, you'd lose the named members, i.e. foo.a is no longer possible.
In this answer, I present another option: If you're willing to pay some space overhead (constant per member), you can have both named members and tuple inheritance simultaneously by inheriting from a tuple of const references, i.e. a const tie of the object itself:
struct Foo : tuple<const int&, const string&> {
int a;
string b;
Foo(int a, string b) :
tuple{std::tie(this->a, this->b)},
a{a}, b{b}
{}
};
This "attached tie" makes it possible to assign a (non-const!) Foo to a tie of convertible component types. Since the "attached tie" is a tuple of references, it automatically assigns the current values of the members, even though you initialized it in the constructor.
Why is the "attached tie" const? Because otherwise, a const Foo could be modified via its attached tie.
Example usage with non-exact component types of the tie (note the long long vs int):
int main()
{
Foo foo(0, "bar");
foo.a = 42;
long long a;
string b;
tie(a, b) = foo;
cout << a << ' ' << b << '\n';
}
will print
42 bar
Live demo
So this solves problems 1. + 3. by introducing some space overhead.
This kind of does what you want right? (assumes that your values can be linked to the types of course...)
#include <tuple>
#include <string>
#include <iostream>
#include <functional>
using namespace std;
struct Foo {
int a;
string b;
template <template<typename ...Args> class tuple, typename ...Args>
operator tuple<Args...>() const {
return forward_as_tuple(get<Args>()...);
}
template <template<typename ...Args> class tuple, typename ...Args>
operator tuple<Args...>() {
return forward_as_tuple(get<Args>()...);
}
private:
// This is hacky, may be there is a way to avoid it...
template <typename T>
T get()
{ static typename remove_reference<T>::type i; return i; }
template <typename T>
T get() const
{ static typename remove_reference<T>::type i; return i; }
};
template <>
int&
Foo::get()
{ return a; }
template <>
string&
Foo::get()
{ return b; }
template <>
int&
Foo::get() const
{ return *const_cast<int*>(&a); }
template <>
string&
Foo::get() const
{ return *const_cast<string*>(&b); }
int main() {
Foo foo { 42, "bar" };
const Foo foo2 { 43, "gah" };
int a;
string b;
tie(a, b) = foo;
cout << a << ", " << b << endl;
tie(a, b) = foo2;
cout << a << ", " << b << endl;
}
Major downside is that each member can only be accessed by their types, now, you could potentially get around this with some other mechanism (for example, define a type per member, and wrap the reference to the type by the member type you want to access..)
Secondly the conversion operator is not explicit, it will convert to any tuple type requested (may be you don't want that..)
Major advantage is that you don't have to explicitly specify the conversion type, it's all deduced...
This code works for me. I'd love it if someone could point out anything wrong with it.
Simple Version on Compiler Explorer
More Generic Version on Compiler Explorer
#include <tuple>
#include <cassert>
struct LevelBounds final
{
int min;
int max;
operator std::tuple<int&, int&>() { return {min, max}; }
};
int main() {
int a, b;
auto lb = LevelBounds{30, 40};
std::tie(a, b) = lb;
assert(30 == a);
assert(40 == b);
return 0;
}

How does the template deduction and decltype(T) works on a refrence?

I am learning template in C++. Something I don't understand is that:
Given a type X, and create an object X a;, if I define a reference to a by X& b = a;, then std::is_reference<decltype(b)>::value returns true. However, is I take b as an argument to a template function, the deducted type is not a reference.
#include <iostream>
class X {};
template<typename T>
void
F(T t)
{
if (std::is_reference<T>::value)
std::cout << "T is a reference" << std::endl;
}
int
main()
{
X a;
X &b = a;
std::cout << std::is_reference<decltype(b)>::value << std::endl; // return true
F(b); //return false
}
My question is why the code works like this.
The type of b as an expression is X due to [expr.type]/1:
If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis.
So T is deduced to be X. However decltype(b) is not simply the type of b as an expression. According to [dcl.type.simple]/4 (irrelevant part is elided by me):
For an expression e, the type denoted by decltype(e) is defined as follows:
...
otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, decltype(e) is the type of the entity named by e. ...
...
Note it's the type of the entity named by b (i.e. X&), not the type of the expression b.
Trying to answer the "why"-part of the question: The function
void someFunc(int arg) { /* ... */ }
clearly receives its argument by value. Having the int in mind, what kind of semantics do you expect from this function?
template<class T> void someFunc(T arg) { /* ... */ }
I would expect the argument to be passed by value, too. Now consider the calling snippets, in the first case
int i;
int& j = i;
someFunc(j); /* j passed by value. */
and in the second case
X a;
X& b = a;
someFunc(b); /* b passed by reference? Luckily not. */
With the function template signature instead being
template<class T> someFunc(T& arg) { /* ... */ }
there is not much potential missunderstanding at the point of invocation - the argument is passed by reference. In the end, this boils down to integers being regular types, while references aren't (see this post for a recent treatise, also covering pointers).

std::function signature pointer vs pointer reference = no difference?

Here is code example:
#include <string>
#include <functional>
struct Foo {};
typedef bool func_type(Foo *&, const std::string&);
typedef std::function<bool(Foo*&, const std::string&)> FunctionalType;
bool f(Foo *, const std::string&)
{
}
int main()
{
#if 1
func_type *func;
func = f;
#else
FunctionalType f2;
f2 = f;
#endif
}
As you see, I have declared function type with "reference to pointer" as the first argument Foo *&, and I expect that function with just "pointer" as the first argument Foo * cannot be assigned to a variable of this type.
The #if 1 region fails to compile (as I expect); however, the alternative did not emit any errors:
FunctionalType f2;
f2 = f;
Why does it compile without error (with at least gcc 5.2 and clang 3.7)?
How it can be fixed, so that std::function<Params> does not accept f for conversion?
std::function<R(Ts...)> is defined as a type whose objects can represent any function that can be called with arguments Ts... and whose return value is convertible to R.
Since your function f can be called with an lvalue of type T* as the first argument (which is the requirement that your Foo *& imposes), it is a valid function to be stored in your std::function.
There is no way to suppress this behavior that I know of.
std::function is a type-erasing container of invokable things.
It will store an instance of any C++ type that can be copied, destroyed and invoked with a "compatible" signature.
In this case, the signature is bool(Foo*&, const std::string&).
The core idea is that when Args... in the R(Args...) part of the std::function type is Foo*&, const std::string&, those arguments can be passed to a function expecting Foo* and const std::string&.
std::function works based on compatibility, not exact matching of signatures.
If you really, really need to ban things that do not take references:
template<class T>
struct reference_only {
T& t;
operator T&(){ return t; }
operator T()=delete;
reference_only(T& tin):t(tin){}
};
then use:
typedef std::function<void(reference_only<Foo*>)> FunctionalType;
which doesn't like being converted to a value-type, but accepts being converted to a reference type (of type Foo*& in this case).
Live example compiling, live example not compiling.

how to decay array type to const pointer type in C++?

I would like to automatically generate const accessor function for given member but I struggle with arrays. It is possible to "decay" array type to a pointer, but I do not know how to make type of pointed value const? Any obvious method of adding const will only apply the pointer. Of course, I can make specialised accessor for array types, but it is not ideal solution. Returning const pointer to const value would also be acceptable. This is example of incomplete accessor:
auto foo() const -> const typename std::decay<decltype(foo_)>::type { return foo_; }
If you intend to get the address of a member array, simply qualify it as const
#include <iostream>
using namespace std;
struct fooType {
};
class MyClass {
public:
fooType foo_[2];
auto foo() const -> typename std::decay<const decltype(foo_)>::type
{ return &foo_[0]; }
};
int main() {
MyClass classObj;
classObj.foo();
return 0;
}
http://ideone.com/PjclAf
Edit:
The documentation states that
Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer
implicit conversions to the type T, removes cv-qualifiers, and defines
the resulting type as the member typedef type. This is the type
conversion applied to all function arguments when passed by value.
(emphasis mine)
The important takeaway here is that std::decay() always act to "simulate" a pass-by-value mechanism with the type you're feeding it. Cv-qualifiers are dropped iff they can be dropped in a pass-by-value call, not if they actually define the resulting type.
Take the following example:
#include <iostream>
#include <type_traits>
template <typename T, typename U>
struct decay_equiv :
std::is_same<typename std::decay<T>::type, U>::type
{};
void function1(int happyX) {
// happyX can be modified, it's just a local variable
happyX = 42;
std::cout << happyX << std::endl;
}
void function2(const int *ptrByValue) {
// ptrByValue can be modified, however its type is 'const int' and that CANNOT be modified
ptrByValue = (const int*)0xDEADBEEF;
std::cout << ptrByValue << std::endl;
}
int main()
{
std::cout << std::boolalpha
<< decay_equiv<const int, int>::value << std::endl // cv-qualifiers are dropped (pass-by-value)
<< decay_equiv<const int[2], int*>::value << std::endl; // cv-qualifiers here CANNOT be dropped, they're part of the type even if passed by value
const int myConstValue = 55;
function1(myConstValue);
const int myArrayToConstValues[2] = {4,2};
function2(myArrayToConstValues);
return 0;
}
http://ideone.com/AW6TJS
In your example you're asking for a constant return value (you can't modify the address of the first element) but asking in the trailing return type for a non-const one, that's why the compiler is complaining and what I just wrote is the reason why the const cannot be dropped by std::decay(): it is part of the type even in a pass-by-value situation (e.g. function2()).

pass reference to array in C++

Can any one help me understand the following code
#include <iostream>
void foo(const char * c)
{
std::cout << "const char *" << std::endl;
}
template <size_t N>
void foo(const char (&t) [N])
{
std::cout << "array ref" << std::endl;
std::cout << sizeof(t) << std::endl;
}
int main()
{
const char t[34] = {'1'};
foo(t);
char d[34] = {'1'};
foo(d);
}
The output is
const char *
array ref
34
Why does the first foo calls the const char * version ? How can I make it call the reference version ?
Conversion of const char[N] to const char* is considered an "exact match" (to make literals easier, mainly), and between two exact matches a non-template function takes precedence.
You can use enable_if and is_array to force it to do what you want.
A messy way to force it might be:
#include <iostream>
template <typename T>
void foo(const T* c)
{
std::cout << "const T*" << std::endl;
}
template <typename T, size_t N>
void foo(const T (&t) [N])
{
std::cout << "array ref" << std::endl;
}
int main()
{
const char t[34] = {'1'};
foo(t);
char d[34] = {'1'};
foo(d);
}
/*
array ref
array ref
*/
I realise that the OP had char not some generic T, but nonetheless this demonstrates that the problem lay in one overload being a template and not the other.
Let's look at this modified example with no template.
void foo(const char * c)
{
std::cout << "const char *" << std::endl;
}
void foo(const char (&t) [34])
{
std::cout << "const char (&) [34]" << std::endl;
}
int main()
{
const char t[34] = {'1'};
foo(t);
}
My compiler says call of overloaded foo is ambiguous. This is because conversions from array to pointer are considered an "Exact" conversion sequence and are not better than the null conversion sequence for overload resolution (Standard section 13.3.3.1.1.)
In the original code, the template parameter N can be deduced as 34, but then both non-template foo(const char*) and foo<34>(const char (&)[34]) are considered in overload resolution. Since neither is better than the other by conversion rules, the non-template function beats the template function.
Fixing things seems tricky. It seems like the is_array template from header <type_traits> (from C++0x if possible or Boost if not) might help.
This appears to be different for various compilers.
Mircosoft and Borland both use the const char* version, while GNU is giving the output you described.
Here is a snippet from the C++ standard:
14.8.2.1 Deducing template arguments from a function call
[temp.deduct.call]
Template argument deduction is done by
comparing each function template
parameter type (call it P) with the
type of the corresponding argument of
the call (call it A) as described
below.
If P is not a reference type:
-- If A is an array type, the pointer type produced by the array-to-pointer
standard conversion (4.2) is used in
place of A for type deduction;
otherwise,
-- If A is a function type, the pointer type produced by the
function-to-pointer standard
conversion (4.3) is used in place of A
for type deduction; otherwise,
-- If A is a cv-qualified type, the top level cv-qualifiers of A's type
are ignored for type deduction.
If P is a cv-qualified type, the top
level cv-qualifiers of P's type are
ignored for type deduction. If P is a
reference type, the type referred to
by P is used for type deduction
The compiler will build an A list as follows:
Argument: t d
A: char const[34] char[34]
And parameter list P:
Parameter: c t
P: char const* char const& t[N]
By default the compiler should choose non-referenced parameters. GNU is dong it wrong the second time for some reason.