Here is a snippet from the documentation of the Boost.Reflect library:
template<typename T>
struct xml_printer {
xml_printer( const T& c ):self(c){}
template<typename Type>
static xml_printer<Type> make( const Type& t ) {
return xml_printer<Type>(t);
}
template<typename Member, typename Class, Member Class::*p>
void operator()( const char* name )const {
std::cerr<<"<"<<name<<">"<<(self.*p)<<"</"<<name<<">\n";
}
const T& self;
};
The part I'm confused about is the declaration of operator() in the visitor:
template<typename Member, typename Class, Member Class::*p>
void operator()( const char* name )const
Particularly the Member Class::*p part. If I understand correctly, this type parameter is in place in order for the visitor to be able to resolve the member, and this is the type information which the library stores somehow for the member. However, it looks very unusual to me with two types written after each other. Could you explain to me how this works or perhaps provide an example that would call a function with such declaration?
Member Class::*p it's pointer to member of class Class with type Member.
So, something like this will call it's true
auto printer = xml_printer<T>();
printer.template operator()<int, T, &T::x>("x");
where x is member variable of type T with type int.
From docs, that you link
#define BOOST_REFLECT_VISIT_MEMBER( r, visitor, elem ) \
visitor.template operator()
<BOOST_TYPEOF(type::elem),type,&type::elem>( BOOST_PP_STRINGIZE(elem) );
Member Class::*p means that p is a pointer to Class member of type Member.
It's unrelated to template declarations, it's just a C++ syntax for pointer-to-member declaration.
Pointer to members are valid template arguments (see C++11 standard 14.1/4).
You'd call it like this:
xml_printer<foo> printer;
printer.operator()<some_type, foo, &foo::bar>("some string literal");
Related
Let's say I have the following class:
class C
{
public:
void f( int ) {}
void f( int ) const {}
};
The only difference between the two overloads of f is that one of them is const. I can't use decltype( &C::f ) because there is still ambiguity.
I want to get the function information by using a traits class, so I need to provide the function type.
You can force the non-const C::f overload to be chosen by passing &C::f to a function that expects a pointer to a non-const member function. To wit,
template <class C, class Ret, class... Args>
// trailing return type can be omitted in C++14
constexpr auto g(Ret (C::*p)(Args...)) -> decltype(p) {
return p;
}
static_assert(std::is_same<void (C::*)(int), decltype(g(&C::f))>::value, "wrong overload");
(Note that additional work needs to be done if you want to support: C-style variadic functions, volatile-qualified functions, or ref-qualified functions.)
Consider the code:
class Character
{
void kill();
void send_to_wall();
}
template <typename T>
void GeorgeFunc(T fp)
{
??? obj;
(obj.*fp)();
}
int main()
{
GeorgeFunc(&Character::kill);
}
So my question here is: how can I get ???? It seems that the compiler definitely knows what this type is (Character) during template instantiation, but I'm not sure how to get at it. My current workaround is to change to: void GeorgeFunc(void (T::*fp)()), but it would be cleaner to simply get the type from the member function pointer. decltype(fp) would return void(Character::*)(), and decltype(fp()) would return void. Any way to get Character?
Yes, just use a trait to determine this.
template <typename> struct member_function_traits;
template <typename Return, typename Object, typename... Args>
struct member_function_traits<Return (Object::*)(Args...)>
{
typedef Return return_type;
typedef Object instance_type;
typedef Object & instance_reference;
// Can mess with Args... if you need to, for example:
static constexpr size_t argument_count = sizeof...(Args);
};
// If you intend to support const member functions you need another specialization.
template <typename Return, typename Object, typename... Args>
struct member_function_traits<Return (Object::*)(Args...) const>
{
typedef Return return_type;
typedef Object instance_type;
typedef Object const & instance_reference;
// Can mess with Args... if you need to, for example:
static constexpr size_t argument_count = sizeof...(Args);
};
Now your declaration is:
typename member_function_traits<T>::instance_type obj;
However, I would argue that since you require a member function pointer (other types would fail to instantiate due to the line (obj.*fp)()1) that your function should take a member function pointer directly instead of a completely generic type.
So this definition would not only work, but I would consider it preferred -- the error messages when someone uses something other than a pointer-to-member-function will be much clearer because the argument type will be incompatible:
template <typename Return, typename Object>
void GeorgeFunc(Return (Object::*fp)())
{
Object obj;
(obj.*fp)();
}
Note that this does allow passing a pointer-to-member-function that returns any type. Since we don't really use the return value, we don't care what it is. There is no reason to enforce that it is void as in your "workaround."
The only downside to using this approach is that you need two overloads if you intend to also accept pointers to member functions that are declared const. The completely generic implementation does not have this limitation. (I've long wished that pointers to const member functions were implicitly convertible to pointers to non-const member functions, but this is currently not allowed by C++.)
1 This isn't 100% true. If you use a completely generic type as you are right now then the caller could theoretically pass a member data pointer instead of a member function pointer. obj.*fp would evaluate as a reference to the data member, and then you would be invoking operator()() on it. As long as the data member's type implemented this operator then the template function GeorgeFunc could be instantiated.
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.
mem_fun and mem_fun_ref and many other member function adaptors can make member functions act like orindinary functions. But there is one restriction that the member function that they call must be a const one. I get to know how to use them, but confused and puzzled by the reasons behind it. Why is it designed in this way?
update:
Sorry for the ambiguity. write an example below.
class A
{
...
//void fun(){cout<<"Fun";} This is not const and the compiler would complain
void fun() const {cout<<"Not fun";}
...
}
vector<A> avec;
...
for_each(avec.begin(),avec.end(),mem_fun_ref(&A::fun));
...
There is no such a restriction. These template functions are overloaded for const and non-const member functions.
For example
template<class S, class T>
mem_fun_t<S,T> mem_fun(S (T::*f)());
template <class S, class T>
const_mem_fun_t<S,T> mem_fun(S (T::*f)() const);
How can I call a may-existing member function by template technology, I am not want to use virtual method, because the class is arbitrary.
For example:
class A {
void setValue(int value);
};
How to define a template to call class A's setValue if it existing, else do nothing.
The template is something like:
template <typename T>
struct call_if_exist {
void invokeOrNot(T& a, int value){ // EXISTING: setValue
a.setValue(value);
}
}
template <typename T>
struct call_if_exist {
void invokeOrNot(T& a, int value){ // NOT EXISTING: do nothing
}
}
It's about invoking, not checking.
You can take advantage of SFINAE to make a class template that checks for the existence of a member function in a given class, and use the result as a boolean flag to specialize your template for the case when there is no function, and the case when the member function exists:
template<typename T , bool member_exists = has_set_value<T>::result>
struct call_if_exist;
template <typename T>
struct call_if_exist<T,true> {
void invokeOrNot(T& a, int value){ // EXISTING: setValue
a.setValue(value);
}
}
template <typename T>
struct call_if_exist<T,false> {
void invokeOrNot(T& a, int value){ // NOT EXISTING: do nothing
}
}
Edit: The has_set_value trait
template<typename T>
class has_set_value
{
typedef struct{ char c[1]; } yes;
typedef struct{ char c[2]; } no;
template<typename U> static yes test(&U::set_value);
template<typename U> static no test(...);
public:
static const bool result = sizeof( test<T>(NULL) ) == sizeof( yes );
};
This class is the typical example of the usage of SFINAE to check for the existence of a member (type or function) of a certain class.
First we define two typedefs, yes and no, which are used to differentiate overload resolutions through the sizeof operator.
The first overload of test() has a pointer to the member function as parameter, and the last is an overload which goal is to be called by everything thats not a pointer to the member. This is done through a variadic-function (Note the ellipsis), which can be used with any kind of parameter.
The point of the implementation is even if the second overload can hold any parameter, the first is an explicit case for pointers to our member function. So if the parameter could be a pointer to the function, the call is resolved to the first function, else its resolved to the second.
As I said before, we use the typedefs to differentiate the overload resolution through the sizeof operator: Note that the first overload returns yes and the later returns no. So if the size of the result type of the call to test() using a pointer (NULL) is equal to the size of yes, means that the overload is resolved to the first, and the class passed as parameter (T) has a member function called set_value.
Alexandrescu's Modern C++ Dessign includes an example of this kind of trait in chapter two to check if one type is implicitly convertible to other.