out of class definition of function template using expression-SFINAE - c++

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.

Related

Template member std::function of none templated class

I have a class that it has a templated function
class DialogManager
{
public:
template<class T>
using TimeoutCallback = std::function<void (T)>;
DialogManager();
virtual ~DialogManager();
template<class T>
void SetTimeoutCallback(TimeoutCallback<T> callback);
int GetDialoge(T obj);
private:
TimeoutCallback mTimeoutCallback;
}
I get below error:
error: invalid use of template-name ‘DialogManager::TimeoutCallback’
without an argument list
I've changed code to
template<class T>
TimeoutCallback<T> mTimeoutCallback;
but I a got another compiler's error
error: data member ‘mTimeoutCallback’ cannot be a member template
example usage is
#include <DialogManager.h>
class foo()
{
public:
foo()
{
DialogManager* dialog = new DialogManager();
}
DialogManager* mDialogManager;
};
struct data
{
data() {}
};
int main()
{
Foo* f1 = new Foo();
auto a1 = f1->mDialogManager->GetDialoge(1);
auto b1 = f1->mDialogManager->GetDialoge("test");
Foo* f2 = new Foo();
data d;
auto a2 = f2->mDialogManager->GetDialoge(d);
auto b2 = f2->mDialogManager->GetDialoge(45.231);
return 0;
}
`
If you want to share the same template type in different parts of class, make it template class instead of template function.
#include <functional>
template<class T>
class DialogManager
{
public:
using TimeoutCallback = std::function<void (T)>;
DialogManager();
virtual ~DialogManager();
void SetTimeoutCallback(TimeoutCallback callback);
private:
TimeoutCallback mTimeoutCallback;
};
If you are writing a class header file, then you generally want to template the class and use the template type's name as a variable type within that file. Then, inside the coder file, you can define the methods similar to how you were in the code your provided. It looks like you fixed the first compilation error, and the second error seems to be because you are templating individual methods within the header file. Here is a basic example for templating a header file:
`template<typename T>
class Node{
private:
T m_entry;
Node<T>* m_nextNodePtr;
public:
T getEntry() const;
void setEntry(T newEntry);
//more
}`
And here is the header file's accompanying coder file:
`template<typename T>
T Node<T>::getEntry() const{
return m_entry;
}
template<typename T>
void Node<T>::setEntry(T newEntry){
m_entry = newEntry;
return;
//more
}`
Hope this helps!
Your mTimeoutCallback is a template member variable; resolving the using, it's a
template<class T>
std::function<void (T)> mTimeoutCallback;
Unfortunately (a) template variables are available only starting from C++14 (and you tagged C++11) and (b) a template variable can be member of a class/struct only if static.
So your errors.

X is not a class template

I am trying to compile the below code, and I am getting the error as
"error: ‘MD5Sum’ is not a class template
template<> struct MD5Sum<::cv_bridge::CvImage>"
template<> struct MD5Sum<::cv_bridge::CvImage>
{
static const char* value() { return MD5Sum<::sensor_msgs::msg::Image>::value(); }
static const char* value(const ::cv_bridge::CvImage&) { return value(); }
static const uint64_t static_value1 = MD5Sum<::sensor_msgs::msg::Image>::static_value1;
static const uint64_t static_value2 = MD5Sum<::sensor_msgs::msg::Image>::static_value2;
// If the definition of sensor_msgs/Image changes, we'll get a compile error here.
ROS_STATIC_ASSERT(MD5Sum<::sensor_msgs::msg::Image>::static_value1 == 0x060021388200f6f0ULL);
ROS_STATIC_ASSERT(MD5Sum<::sensor_msgs::msg::Image>::static_value2 == 0xf447d0fcd9c64743ULL);
};
This seems to be template specialization. Does it means that template class should be present first before make template specialization out of it.
The class template MD5Sum needs to be declared before any of its specializations.
You either need to include the file where the template is declared, or declare it yourself.
You can use an empty definition (if you only want to call fully specialized versions) or the generic implementation you choose:
// add this before your specialization
template <class T> struct MD5Sum; // empty declaration
// or your default implementation
template <class T> struct MD5Sum {
...
};

How to access static of templated class from template?

I am trying to write a template using the CRTP pattern. What I would like is for the template to access a static const in the templated class.
My template look like this in it's own header:
template <class T> foo {
static const int readValue = T::value
}
And I inherit from the template like this (in another header file):
class fooImpl: foo<fooImpl> {
static const int value = 42;
}
However, clang complains:
No member named 'value' in 'fooImpl'
I think I get the chicken and egg problem here. The template does not know the definition of fooImpl, and hence, cannot know that it has the member value when it is instantiated.
But how do I work around it? Is there a way to get the compile time propagation of the const value into the instantiated template?
The foo<fooImpl> base class is instantiated in the base-class list but fooImpl is an incomplete type at that point, and foo::value has not been declared yet.
You can move the definition of the static member later:
template <class T> struct foo {
static const int readValue;
};
class fooImpl: foo<fooImpl> {
static const int value = 42;
};
template<class T> const int foo<T>::readValue = T::value;
However, this won't allow you to use readValue as a compile-time constant in the body of foo.

How to remove const qualifier from a member function pointer

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.

Why does this code give the error, "template specialization requires 'template<>'"?

When I try to compile this with Clang
template<class T>
struct Field
{
char const *name;
Field(char const *name) : name(name) { }
};
template<class Derived>
class CRTP { static Field<Derived> const _field; };
class Class : public CRTP<Class> { };
Field<Class> const CRTP<Class>::_field("blah");
int main() { }
I get
error: template specialization requires 'template<>'
Field<Class> const CRTP<Class>::_field("blah");
~~~~~~~~~~~ ^
I don't understand the error at all. What is wrong with my definition of _field and how do I fix it?
(Note that the arguments to _field are not necessarily the same for all subclasses.)
For the compiler to identify this as a template specialization (e.g. to be able to check the syntax), you need the template keyword:
template<>
Field<Class> const CRTP<Class>::_field("blah");
Its brackets are empty as all template parameters are specialized, but you cannot just leave it away.
The error says exactly what is missing. template<> is missing before that line.
template<>
Field<Class> const CRTP<Class>::_field("blah");
Note, however, that your typing of Field<Class>, if unique, could be used to construct all instances of Field<Class> with a given string.
template<typename T>
struct field_trait;
template<class T>
struct Field
{
char const *name;
Field() : name(field_trait<T>::get_name()) {}
};
template<class Derived>
class CRTP { static Field<Derived> const _field; };
template<class Derived>
class CRTP<Derived>::_field;
class Class;
template<>
struct field_traits<Class> {
static const char* get_name() { return "blah"; }
};
class Class : public CRTP<Class> { };
int main() { }
which means that every instance of Field<Class> always has the name "blah".
One question I would have is, do you really need storage for said Field<Class> to actually have a pointer to a string, and if so does it need to be unique, and if so does it need to be "bare"? Because figuring out where the static instance exists is somewhat annoying.
Together with field_traits above:
template<class Derived>
class CRTP { static Field<Derived>& field() const { static Field<Derived> _field( field_traits<Derived>::get_name()); return _field; };
this moves the problem of "where is the _field stored" to being the compilers problem. And it is initialized by the contents of field_traits<T>::get_name().
A static data member must have both a declaration and a definition. If this was a plain class it would look like this:
// header:
class C {
static int i;
};
// source:
int C::i = 3;
Templates aren't ordinarily defined in source files, so the code would look something like this:
// header:
template <class T>
class C {
static int i;
};
template <class T>
int C<T>::i = 3;
In your code, you don't have the definition of the static data member. That's okay if you don't use it. But the code that the compiler is complaining about defines a static data member for CRTP<Class>; that's a specialization (because it's not applicable to all instantiations of CRTP, just to this one), and the compiler is saying that you have to tell it that it's a specialization. So do as you're told:
template <>
Field<Class> const CRTP<Class>::_field("blah");
or, to write the non-specialized template version, use the usual template syntax:
template <class T>
Field<T> const CRTP<T>::_field("blah");