How to remove const qualifier from a member function pointer - c++

I'm using a library which contains the following code:
template <typename M>
void _register_member(lua_State *state,
const char *member_name,
M T::*member) {
std::function<M(T*)> lambda_get = [member](T *t) {
//^ error here
return t->*member;
};
//...
However this code does not accept const member function pointers. Passing those yields the error Function cannot return function type 'void () const' or whatever the type of the const member function is.
How do I remove the const qualifier from the passed member function or how do I apply std::remove_const?

As Adam S noted in comments this error occurs when he tries to compile this simple code which uses the library Selene:
#include <selene.h>
class C {
public:
bool get() const;
};
bool C::get() const {return true;}
int main() {
sel::State state;
state["C"].SetClass<C>("get", &C::get);
}
The compiler fails to compile the code in Class.h header. There are two overloads of function member _register_member of the class Class in it:
template <typename T,
typename A,
typename... Members>
class Class : public BaseClass {
private:
// ...
template <typename M>
void _register_member(lua_State *state,
const char *member_name,
M T::*member) {
// ...
}
template <typename Ret, typename... Args>
void _register_member(lua_State *state,
const char *fun_name,
Ret(T::*fun)(Args...)) {
// ...
}
// ...
};
The compiler can't choose the second overload when a pointer to a const function member is passed as a third argument. There should be another overload which could accept a const function member. It should be declared as follows:
template <typename Ret, typename... Args>
void _register_member(lua_State *state,
const char *fun_name,
Ret(T::*fun)(Args...) const)
^^^^^
Without such overload the compiler chooses the first overload which is created to work with pointers to data members (not function members) and fails to compile its code.
So you can't deal with const function members when using current version of Selena library (in such way as you do it at least).

I should mention that for those viewing this now, this was in fact a bug in my code (an oversight really) and it was fixed shortly after the problem was identified.

Related

out of class definition of function template using expression-SFINAE

I'm trying to implement a simple serializer class, which has a Serialize function that dispatches the actual serialization to different overloaded function templates, selected at compile-time using expression-SFINAE with decltype:
#ifndef SERIALIZER_H
#define SERIALIZER_H
#include <string>
#include <utility>
/**** base/primary template for partial/explicit specialization on serializable classes ****/
template <typename T>
struct SerializeHelper;
/**** base abstract serialize class ****/
class Serializer
{
public:
// main serializer member function template
template <typename T>
void Serialize(const std::string &name, const T &value) const;
private:
virtual void Prepare(const std::string &name) const = 0;
virtual void Finalize(const std::string &name) const = 0;
// natively supported types
template <typename T>
auto Serialize(const T &value) const -> decltype(SerializeNative(value), std::declval<void>());
// use static function in class template specialized for type (partial or explicit specialization)
template <typename T>
auto Serialize(const T &value) const -> decltype(SerializeHelper<T>::Serialize(*this, value), std::declval<void>());
// use serializable type interface
template <typename T>
auto Serialize(const T &value) const -> decltype(value.Serialize(*this), std::declval<void>());
private:
// virtual functions for natively supported types
virtual void SerializeNative(int value) const = 0;
virtual void SerializeNative(float value) const = 0;
virtual void SerializeNative(double value) const = 0;
virtual void SerializeNative(const std::string &value) const = 0;
protected:
Serializer() = default;
};
template <typename T>
void Serializer::Serialize(const std::string &name, const T &value) const
{
Prepare(name);
Serialize(value);
Finalize(name);
}
// natively supported types
template <typename T>
auto Serializer::Serialize(const T &value) const -> decltype(SerializeNative(value), std::declval<void>()) // COMPILER ERROR
{
SerializeNative(value);
}
// use serialize function specialized for type
template <typename T>
auto Serializer::Serialize(const T &value) const -> decltype(SerializeHelper<T>::Serialize(*this, value), std::declval<void>())
{
SerializeHelper<T>::Serialize(*this, value);
}
// use serializable type interface
template <typename T>
auto Serializer::Serialize(const T &value) const -> decltype(value.Serialize(*this), std::declval<void>())
{
value.Serialize(*this);
}
#endif // SERIALIZER_H
Problem is, this code doesn't compile, since the compiler complains that the out of class definition of Serialize for native types doesn't have a corresponding declaration inside the class:
In file included from main.cpp:1:
serializer.hpp:53:6: error: no declaration matches 'decltype ((((const Serializer*)this)->Serializer::SerializeNative(value), declval<void>())) Serializer::Serialize(const T&) const'
auto Serializer::Serialize(const T &value) const -> decltype(SerializeNative(value), std::declval<void>())
If I put the inline definition inside the class it compiles fine. It happens with GCC and VC++.
EDIT
the code works fine if I declare the SerializeNative member functions before the member function template declaration, seems like since the call to the SerializeNative function is inside the Serialize function header (in decltype) it needs to see the declaration.
The reason the compiler cannot match the definition with the declaration is the following:
[basic.lookup.unqual]/p7:
A name used in the definition of a class X outside of a complete-class context ([class.mem]) of X shall be declared in one of the following ways:
before its use in class X or be a member of a base class of X ([class.member.lookup]), or
[...]
where [class.mem]/p6:
A complete-class context of a class is a
function body ([dcl.fct.def.general]),
default argument,
noexcept-specifier, or
default member initializer
within the member-specification of the class.
That is, at the point of declaration:
template <typename T>
auto Serialize(const T &value) const -> decltype(SerializeNative(value), std::declval<void>());
the name SerializeNative is not found by the name lookup, because SerializeNative is declared after its use, while it is found in the definition, causing the mismatch.
In order to use SerializeNative in the expression SFINAE, you need to declare the private virtual functions before using their name in the return type of Serialize.
The error for SerializeNative(value) is not immediately reported, because that function could potentially be found in argument-dependent lookup, as soon as the type for value is known.

const/non-const member function based on compile time conditions

Let's consider following classes:
struct InputArgument{};
struct SpecialInputArgument{};
struct MoreSpecialInputArgument{};
struct OutputArgument{};
struct SpecialOutputArgument{};
struct MoreSpecialOutputArgument{};
I need to have a member function that accepts all previous classes as arguments and act on them. To simplify the implementation (don't repeat same code over and over) I made the member function template and dispatched the actual code to non-member functions:
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(T&, const InputArgument&)
{
}
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(const T&, OutputArgument&)
{
}
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(T&, const SpecialInputArgument&)
{
}
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(const T&, SpecialOutputArgument&)
{
}
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(T&, const MoreSpecialInputArgument&)
{
}
template<typename T>
typename std::enable_if<std::is_fundamental<T>::value>::type DoSomething(const T&, MoreSpecialOutputArgument&)
{
}
struct MyGloriuosClass
{
template<typename T>
void DoSomething(T& arg)
{
::DoSomething(myIntMember, arg);
::DoSomething(myFloatMember, arg);
}
int myIntMember = 0;
float myFloatMember = 0.f;
};
And this works perfect:
MyGloriuosClass myGloriuosObject;
InputArgument inputArgument;
SpecialInputArgument specialInputArgument;
MoreSpecialInputArgument moreSpecialInputArgument;
OutputArgument outputArgument;
SpecialOutputArgument specialOutputArgument;
MoreSpecialOutputArgument moreSpecialOutputArgument;
myGloriuosObject.DoSomething(inputArgument);
myGloriuosObject.DoSomething(specialInputArgument);
myGloriuosObject.DoSomething(moreSpecialInputArgument);
myGloriuosObject.DoSomething(outputArgument);
myGloriuosObject.DoSomething(specialOutputArgument);
myGloriuosObject.DoSomething(moreSpecialOutputArgument);
Expect in one case, when the object I use is const:
const MyGloriuosClass myConstGloriousObject = MyGloriuosClass();
myConstGloriousObject.DoSomething(outputArgument);
myConstGloriousObject.DoSomething(specialOutputArgument);
myConstGloriousObject.DoSomething(moreSpecialOutputArgument);
As you can see, all the actual code is done in functions that accept const objects when the argument is of type Output so there is no reason to limit this to only non-const objects or to write my member function twice, once as const and once as non-const. In my ideal world I will deduce if the function is const/non-const based on the type trait std::is_base_of of the argument, but I don't know if this is possible or not.
Is it possible to declare a member function const/non-const based on compile time conditions?
A member function is either const, or non-const member function. There is no third option. Classes can define either a const function, a non-const function, or even both, as you know.
What I suspect that you might be missing is that a const member function can be invoked for a non-const class instance.
So, if your member function does not need to modify any other members of the class instance, just declare your member function as a const function, and it can be invoked for either a const or a non-const class instance.

how to specialize template taking any type to allow variable length arrays

Not even sure what the right terminology for this situation is. If I knew it then I probably would have found the answer.
I have a templatized function that looks like
template<typename T> void write(Buffer& b, const T& value);
There is no generic implementations of this, only explicit specializations. E.g.
template<> void write<int>(Buffer& b, const int& value) { ... }
template<> void write<const char *>(Buffer& b, const char * const & value) { ... }
So far so good. But of course when I call as write(buffer, "hello") I get a link error complaining
undefined reference to `void write<char [6]>(Buffer&, char const (&) [6])'
This error makes sense to me. But what I cannot figure out is how to declare a specialization for this. All I want such a specialization to do is cast the char[] argument to (const char*) and call that existing specialization. But I cannot figure out how to do this.
For the specific example I can do
template<size_t N> void write<char[6]>(Buffer& b, char const (&value) [6])
but it's obviously not acceptable to define one of these for every possible length string constant. But I've tried the following, some of which are just grasping for straws, and none of which work.
template<size_t N> void write<char const[N]>(Buffer& ob, char const (&value) [N])
template<size_t N> template<> void write<char const[N]>(Buffer& ob, char const (&value) [N])
template<> template<size_t N> void write<char const[N]>(Buffer& ob, char const (&value) [N])
template<template<size_t N>> void write<char const[N]>(Buffer& ob, char const (&value) [N])
What's the proper incantation? I'm using GCC 4.7.3 x86_64.
If it is OK, not to specify the argument type when calling the write() function with a character array (or to use char const* in these cases rather than, e.g., char[6]), the way to implement this is to use an overload to deal with char const*:
void write(Buffer& ob, char const* value) {
write<char const*>(ob, value);
}
This function is preferred over the template instantiations and can, thus, be used to suitably delegate the processing for string literals (or C-strings in general, actually).
If it is necessary to get the correct type passed, the only way which would work is to not specialize the actual function template but, instead, to immediately delegate a struct with a suitable member function and to partially specialize this struct to deal with arrays: you cannot partially specialize a function template, i.e., the following is illegal:
template <int N>
void write<char const[N]>(Buffer& ob, char const (&value)[N]); // ERROR
... but it can be simulated by partially specializing a struct instead.
You can't do partial specialization of template functions. See this explanation for how to do a level of indirection to a function in a templated class in order to get around this limitation.
In this simple case you could also get by with a non-template write that invokes the template one:
void write(Buffer &b), const char *v)
{
write<const char *>(b, v);
}
This effectively hides the cast that would have worked with your template function in an inline function.

Explicit specialization using variadic parameters within variadic templated class [MSVS '12: Nov. '12 CTP: error C3522]

I'm trying to expand a class's variadic template type list within a child method as such:
template<typename... P>
struct Foo
{
template<P...> // error C3522: 'P' : parameter
// pack cannot be expanded in this context
static void Bar(P... a){}
};
What is wrong with this code, or is it just a MSVS '12: Nov. '12 CTP bug?
(Yes, I know the explicit template specialization in this example is redundant.)
The above is the simplest case that I get to reproduce the error. The full code is:
template<typename FuncSignature>
class Callback;
template<typename R, typename... P>
class Callback<R (P...)>
{
public:
Callback() : func(0), obj(0) {}
Callback& operator=(const Callback& rhs)
{ obj = rhs.obj; func = rhs.func; return *this; }
private:
typedef R (*FuncType)(const void*, P...);
Callback(FuncType f, const void* o) : func(f), obj(o) {}
private:
FuncType func;
const void* obj;
template<typename FR, typename... FP>
friend class FreeCallbackFactory;
};
template<typename R, typename... P>
class FreeCallbackFactory
{
private:
template<R (*Func)(P...)>
static R Wrapper(const void*, P... a)
{
return (*Func)(a...);
}
public:
template<R (*Func)(P...)>
inline static Callback<R (P...)> Bind()
{
return Callback<R (P...)>
(&FreeCallbackFactory::Wrapper<Func>, 0);
}
};
template<typename R, typename... P>
inline FreeCallbackFactory<R, P...>
GetCallbackFactory(R (*)(P...))
{
return FreeCallbackFactory<R, P...>();
}
void Test(){}
int main(int argc, char** argv){
Callback<void ()> cb = GetCallbackFactory(&Test).Bind<&Test>()
}
It compiles fine in g++ so I'm assuming just a compiler bug, and continued findings still only point to this, are there any possible workarounds for this other than explicitly expanding them out one by one?
Edit: This has been reported to the compiler team as a bug and a patch will be in the next release of the compiler. [Link]
The code looks correct put I doubt that it does what you intended it to do: The declaration
template <P...>
static void Bar(P... a);
declares a function taking P... values as template argument and as function argument. That is, the elements of P need to be of a type allowing non-type parameters (e.g., integers, pointers, or references) and you'd need to provide their respective values as template parameters when calling the function. The call to a function like this would look something like this (although it seems neither gcc nor clang require the template parameters to be passed):
Foo<int, int>::Bar<1, 2>(3, 4);
That said, based on the error messages generated by both gcc and clang it seems that they won't let you create a specialization of member function templates but I haven't verified this in the standard. I think, you should probably just leave out the template declaration and use
static void Bar(P... a);
To me the code does not look correct. The pack expansion P... expands to a set of types. Depending on what you aim to achieve the correct thing to do would be to remove the template<P...> part, in which case each class template instance has one Bar() method with the same parameters as the class. The other would be that this is an unrelated parameter pack in which case an unbounded set of methods is generated, in that case you must declare the method something like:
template<typename... U> static void Bar(U...);
reusing the name P would not be very useful, I guess.

Template Specialization of Template method

OK I have:
template<typename T>
class Reader
{
class Input
{
template<typename C>
void operator()(C& val) const
{
/* Do Some Work */
}
};
};
Unfortunately the generic version of "Do Some Work" does not work for me. Nor it is easy to modify because it is in the middle of some heavy template meta programming code.
So I though I could specialize the method for my type. So my first step was to try and pull the general method out of the class.
template<typename T>
class Reader
{
class Input
{
template<typename C>
void operator()(C& val) const;
};
};
template<typename T>
template<typename C>
void typename Reader<T>::Input template operator()<C>(C& val) const // LINE 13
{
/* Do Some Work */
}
Unfortunately I am getting the error:
s.h:13: error: error: expected ‘)’ before ‘&’ token
Just write it like normal
template<typename T>
template<typename C>
void Reader<T>::Input::operator()(C& val) const // LINE 13
{
/* Do Some Work */
}
Defining the generic version out of class doesn't help you with providing special versions of it though, or I do miss the goal of yours here.
I guess it is impossible as it counts as partial function template specialization, which is not allowed. void Reader<T>::Input::operator () (C& int) has an implicit first argument (the this pointer) of type Reader<T>::Input *, hence its signature is in fact void (Reader<T>::Input *, C &). You are trying to specify C, yet not T.