c++: portable solution to cast and compare member-function pointers - c++

Before I ask what I want to know, here's a little background:
I'm wrapping a std::function in my own class Function, which stores some additional data along with the std::function object. Later on, I have a std::vector<Function<Signature>> that stores a whole bunch of Function's of the same signature (e.g. void(int)). These std::function's may represent normal free functions, functors, or member functions that have been bound to an object using std::bind.
At some later point, I want to traverse the std::vector, and check some properties of the Function objects. For example, I want to select every Function that is bound to a particular member-function (regardless of the object it will be called on). This means I have to find a way to store these member-function pointers, while discarding their exact type. In other words, I need to be able to store a pointer of type void (A::*)() and another pointer of type void (B::*)() in the same field.
I used to do this using a union to 'cast' the member-function-pointer to a void*, but then found out that member-function-pointers are implementation-defined and don't have the same size as pointers. Now I'm looking for a portable solution, and this is what I came up with:
class MemFunPtr
{
friend bool operator==(MemFunPtr const &lhs, MemFunPtr const &rhs);
enum { SIZE = sizeof(void(MemFunPtr::*)()) };
char buf[SIZE];
public:
template <typename T, typename R, typename ... Args>
MemFunPtr(R (T::*ptr)(Args ...))
{
union
{
R (T::*memfcn_)(Args ...);
char buf_[SIZE];
} caster;
caster.memfcn_ = ptr;
memcpy(buf, caster.buf_, SIZE);
}
};
bool operator==(MemFunPtr const &lhs, MemFunPtr const &rhs)
{
return memcmp(lhs.buf, rhs.buf, MemFunPtr::SIZE) == 0;
}
Now my question is, if this is portable. I would be even more happy with a more straightforward way of doing this. I looked in to std::mem_fn, but it seems that the type of these objects is unspecified (the examples on cppreference.com use auto), so I don't think this is where the solution lies.

Related

std::is_const & Co with value-type wrappers as std::reference_wrapper

This might be a bit of an academic example (in the sense that I don't see it having a real use case as-is), but I have come across this line of thought a couple of times without coming up with a satisfying answer.
For the sake of argument, let's suppose that I have a template function that I want to behave differently depending on whether the passed value is const or not. A very simple example might be
template< typename T > void print_constness(T&& t) {
if constexpr (std::is_const_v< decltype(t) >) {
std::cout << "T is const\n";
} else {
std::cout << "T is NOT const\n";
}
}
If I pass a mutable reference to this function, it will correctly detect it as non-const. If I pass a const reference to it, then it correctly detects it as const (provided I can prevent the function from making a copy, e.g. by deleting the copy constructor).
Conceptually, std::reference_wrapper< T > is supposed to represent the same type as const T &. Therefore, one might expect that the result from passing a const T to that function is the same as passing a std::reference< const T >.
But this is not the case, since the wrapper itself is not const. However, for practical purposes, it is. Consider e.g. a template function that has to call a const or non-const overload of a function. When passed a std::reference_wrapper< const T >, it will pass it to the non-const overload and as soon as that tries to access the reference the compiler will complain (rightfully so).
(Note that I deliberately ignored that you can overload on the constness of your argument - my above example shall only serve as an illustration).
My question is: How to detect and in further steps modify constness of value-type wrappers such as std::reference_wrapper when the standard std::is_const, std::add_const and std::remove_const clearly don't work?
Is there a generic/standard solution to this problem or would it require implementing custom is_const, ... traits that specialize on the value wrappers that one expects to encounter?
If so: is it perhaps possible for the implementers of such wrappers to specialize the std type traits so they produce the (semantically) expected result? I kinda expect this to be forbidden...
If you have C++20, there's std::unwrap_reference:
#include <type_traits>
template<typename T>
using remove_reference_and_wrapper_t =
std::remove_reference_t<std::unwrap_reference_t<std::remove_reference_t<T>>>;
template<typename T>
constexpr static bool is_semantically_const_v =
std::is_const_v<remove_reference_and_wrapper_t<T>>;
static_assert(is_semantically_const_v<const int>);
static_assert(is_semantically_const_v<const int&>);
static_assert(is_semantically_const_v<const int&&>);
static_assert(!is_semantically_const_v<int>);
static_assert(!is_semantically_const_v<int&>);
static_assert(!is_semantically_const_v<int&&>);
static_assert(is_semantically_const_v<std::reference_wrapper<const int>>);
static_assert(!is_semantically_const_v<std::reference_wrapper<int>>);
It's a little unwieldy, but it works.
You can then use the type returned by remove_reference_and_wrapper_t to further manipulate the object; i.e., get a reference to the actual object:
remove_reference_and_wrapper_t<decltype(t)>& underlying = t;

Is it possible to distinguish a `const` variable from a non-`const` one and from a `const&` reference?

Suppose I wanted to make my own reference ("smart pointer") type which is guaranteed to always refer to immutable data, rather than merely immutably-viewed data. In other words, data which can't be mutated by anyone, as opposed to just not through that particular reference. This is not so easy, because C++ generally considers const things to be a subcase of mutable-access things, and & references implicitly convert to const &, likewise * to const *.
Let's call this type:
template<typename T>
class ImmRef { ... };
One straightforward thing I can do is to declare:
template<typename T>
struct Imm
{
const T value;
};
and then only allow creating ImmRefs out of Imms, or other ImmRefs. Here the variable itself is declared const, so it's not possible to make any mutable references to it, and it is in fact truly immutable. (Unless it internally uses mutable, but since there's nothing we can do about that, let's ignore it.)
That works, but for greater flexibility, wider applicability, and compatibility with other code which doesn't know about our Imm type, it would be much better if we could create ImmRefs to any const-declared variable. But it's not clear whether C++ makes it possible to distinguish "a variable that is declared const" from "a const reference to a variable", or even from "a variable that is not declared const".
In other words, this should work:
const int myConstNumber = 666;
ImmRef<int> myImmRef = immRef(myConstNumber);
But this should not:
int myNumber = 666;
ImmRef<int> myImmRef = immRef(myNumber);
And this should not:
const int& myConstRef = myNumber;
ImmRef<int> myImmRef = immRef(myConstRef);
Is there any dark template magic which lets me do this?
There's no way to tell that a reference refers to a real const object. And there's no way for a function to tell whether its argument names an object or a reference.
decltype will tell you specifically that a name belongs to a const object (not a reference), but the name must be in scope. This usage can't be encapsulated in a template.
A macro will do the job, if you really really want it:
#define MAKE_IMMREF( NAME ) ImmRef< decltype( NAME ) >{ NAME }
template<typename T>
struct ImmRef {
static_assert ( std::is_const< T >::value, "ImmRef requires a constant." );
T & ref;
};
Demo.

allocator_traits::construct() vs allocator_traits::allocate()

C++11 provides the std::allocator_traits class as the standard way to use allocators. The static function std::allocator_traits::construct() takes a pointer to where the object should be constructed. The std::allocator_traits::allocate() static function, however, returns an allocator::pointer value, which only has to behave like a pointer but it is not necessarily one (in general, although std::allocator::pointer is required to be a pointer).
How is one supposed to use the allocation and construction static methods if, in general, they will work with incompatible types? Can they be used only if the pointer type is actually convertible to a normal plain pointer?
There are two techniques to do this depending on what you have at the moment.
If you have an lvalue expression, say the value field in a node, then you can use std::addressof like so:
allocator_traits<allocator_type>::construct(alloc, std::addressof(ptr->value), ...);
where ptr is an allocator_type::pointer.
However if you don't have a field to dereference and you want to convert an allocator_type::pointer to T*, there's a trick you need to implement first:
template <class T>
inline
T*
to_raw_pointer(T* p) noexcept
{
return p;
}
template <class Pointer>
inline
typename std::pointer_traits<Pointer>::element_type*
to_raw_pointer(Pointer p) noexcept
{
return p != nullptr ? ::to_raw_pointer(p.operator->())
: nullptr;
}
And now you can say:
allocator_traits<allocator_type>::construct(alloc, to_raw_pointer(ptr), ...);
Starting with C++20, there is std::to_address, proposed in P0653.

How do I compare two generic types in C++?

I need to determine whether an element is the same as the one I'm passing by reference.
In the function Belongs I need to compare equality between d is and an element of a stored in a dynamic list:
struct Nodo{ Dominio dominio; Rando rango; Nodo* next; };
typedef Nodo* ptrNodo;
ptrNodo pri;
template<class Dominio, class Rango>
bool DicListas<Dominio,Rango>::Belongs(const Dominio &d)
{
bool retorno = false;
if(!EsVacia())
{
ptrNodo aux=pri;
while(aux!=NULL)
{
if(aux->dominio==d)//-------> THIS CLASS DOESN'T KNOW HOW TO COMPARE THE TYPE DOMINIO.
{
retorno = aux->isDef;
}
aux = aux->sig;
}
}
return retorno;
}
Whatever type argument you provide for the type parameter Dominio, you've to overload operator== for that type.
Suppose, you write this:
DicListas<A,B> obj;
obj.Belongs(A());
then you've to overload operator== for the type A as:
class A
{
public:
bool operator == (const A &a) const
{
//compare this and a.. and return true or false
}
};
Also note that it should be public if it's a member function, and better make it const function as well, so that you can compare const objects of type A.
Instead of making it member function, you can make operator== a non-member function as well:
bool operator == (const A &left, const A & right)
{
//compare left and right.. and return true or false
}
I would prefer the latter.
It reduces to defining an overload of operator== for the user-defined type:
bool operator==(const WhateverType &a, const WhateverType &b)
{
return whatever;
}
or maybe as a member of WhateverType.
If you want to compare something about two, possibly distinct, types, you probably want to look at either Boost type traits or the versions of type traits that made it into TR1 and C++11 (if you're using a compiler that supports either TR1 or C++11).
However, that doesn't seem to be the case that you're running into. In your case, you know that the two objects are of the same type. In C++, you will get compiler errors if a class you pass as a type parameter to a template does not support all the methods or operators that the template needs. That's what you're running into. That's also the problem that concepts are meant to solve (well, concepts are meant to advertise "if you want to use your type with this template, then your type must support ..."). But, unfortunately we didn't get concepts in C++11, so the requirements are implicit. In your case, as already mentioned, you simply need to make sure that whatever class you pass in as Dominio supports operator==.
You may also want to look at Boost concept check to advertise that whatever type is passed in as Dominio must support operator==.

C++ how to pass method as a template argument

Suppose I have a class X:
class X {
// ...
size_t hash() const { return ...; }
};
I would like to create a std::tr1::unordered_map<X, int, HashFn> where I want to pass in
X::hash() as HashFn. I know I can declare my own functor object. I feel that
there should be a way to do this by directly passing a pointer to X::hash().
Is there?
No; as you've shown it, you need a small utility struct:
#include <functional>
template<typename T, std::size_t (T::*HashFunc)() const = &T::hash>
struct hasher : std::unary_function<T, std::size_t>
{
std::size_t operator ()(T const& t) const
{
return (t.*HashFunc)();
}
};
Then you can create an unordered_map like so:
std::tr1::unordered_map<X, int, hasher<X> > m;
No, there isn't. The reason is that whatever is used as your HashFn must take a single argument which is a const reference to an object in the container. X::hash takes a single argument which is a const pointer to an object in the container (the this pointer is an implicit first argument in this case), so using that function by it self is not possible.
You probably use some bind magic, using boost::lambda and boost::bind. I'm not exactly sure how, but it would probably look something like this:
boost::bind(&X::hash, &_1);
Which creates a function object which will call X::hash with a pointer.
size_t hash() const { return ...;}
A function which calculates hash value takes one parameter of type Key which your function doesn't take. Hence its signature is wrong to begin with.
Since you want to implement a function, rather than a functor, here is how it should be done:
size_t hash(const KeyType &key)
{
return /*calculate hash and return it*/;
}
Make it static member function and pass it as X::hash or make it a free function, is your choice.
You can't directly, but you can wrap it. The easy way to do so is to use boost::mem_fn(), or the standard equivalents if your compiler supports them: tr1::mem_fn() (from TR1) or std::mem_fn() (from C++11).
EDIT: Actually it's not so simple. mem_fn() will work fine for a function parameter, but since its return type is unspecified it's difficult to use as a template parameter. If you have C++11 support you could use decltype to find the type; otherwise you're probably best off writing your own function object as you mentioned.