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.
Related
I'm trying to create a class template that identifies functions, in which I can identify when a function is specializing the class model for R (*) (), but in std::function you can declare return_type (), and std::is_same< int(), int (*) () >.::value is zero.What does this int () statement mean and what is the difference between int () and int (*) ()?Updated: So int () is function declaration or function type and int (*)() is pointer to function.But waht is the type function of int (std::string::)()?It's something like int std::string::(), or like in std::function int(const std::string&)?How i can make this program output 1?
#include <iostream>
template<typename A,typename B>
struct IsSame{
enum{
value = 0};
};
template<typename A>
struct IsSame<A,A>{
enum{
value = 1
};
};
typedef int (SumType)(int,int)const;
class X{
public:
SumType sum;
};
int X::sum(int a,int b)const{
return a+b;
}
int main()
{
std::cout << IsSame< int (const std::string&,int,int)const,
decltype( &X::sum)>::value;
}
A function has a type like void() and if you take the address of that you get a pointer to that function void(*)(). They are not the same type, although a function can decay to a pointer to a function in a similar way that arrays decay to pointers to the first member.
This means you can declare a function:
void f();
and assign it to a pointer to a function (of the same signature):
void (*p_to_f)() = f;
and the function decays to a pointer to a function.
This is why I think it can be difficult to understand the difference between void() and void(*)(), but they are distinct types, and that difference can be important in templates (where this decay doesn't necessarily occur).
One important example of when decay doesn't happen is when matching template specialisations. Consider what std::function does for example:
template <class>
class function;
template <class R, class ... Args>
class function<R(Args...)> { /* ... stuff ... */ };
The fact that the specialisation is on a function type is different than if it were specialised on a pointer-to-function type.
function<void(int,int)> f;//matches specialisation
function<void(*)(int,int)> g;//does not match specialisation
In response to the OP's comment (now deleted):
In order to also match pointers to functions you can do:
template <class R, class ... Args>
function<R(*)(Args...)> : function<R(Args...>{};
which essentially means you treat functions and pointers to functions the same. If you want to match as many functions as you can, you also have to consider member functions, const member functions and noexcept functions (as of C++17). If you want to be completely thorough, there's also &, &&, const &, const&&, volatile, etc... but I wouldn't worry about those unless they actually come up.
I'm writing a C++ template that needs two params: typename T, and an arbitrary function that maps T to an unsigned int.
How can I declare and use a template that can do that? I'd like to keep it simple, so that any dumb function can be used.
UPDATE:
Here is an example of what I'd like to do:
template<typename T, function f> // f has signature: unsigned int f(T);
class SortedContainer {
...
}
And, in this file:
unsigned int weight(Package p) { return p.w; }
SortedContainer<Package, &weight> sc;
UPDATE upon writing code
Based on the answers, I tried writing code, but it won't compile. Or rather, the template will compile, but not the test which invokes it.
The template code looks like this:
template<typename T, typename f>
class C {
...f(T)...
...
The invocation code looks like:
struct S {
int operator()(const int n) {
return n; // Dummy test code
}
};
...C<int, S>&...
The error message is:
error: no matching function for call to 'S::S(const int&)'
note: candidates are:
note: S::S()
It seems like it's trying to use S's constructor for some reason, as opposed to using the operator() which I want it to do.
The purpose of the f parameter is that the SortedContainer needs to be able to position T by an integer value. T is not necessarily an integer or even Comparable, so the caller, when instantiating a SortedContainer, needs to pass not only type T, but a function f to transform T to an integer.
The common way of doing this is to accept a general type F for the function. This will allow any kind of function-like object, whether it is a function pointer or a class object with an overloaded operator(). So:
template<class T, class F>
class SortedContainer {
// ...
}
Compare with things like std::map which does exactly this.
The disadvantage of this is that you cannot control what the prototype of the function is. This may or may not be a problem. One way is just to use it as if it was T-to-unsigned int and rely on the fact that the type system will catch any errors at the point of use.
Another way would be to verify the constraint with some kind of type trait. An example:
static_assert(std::is_same<unsigned int,
typename std::result_of<F(T)>::type>::value,
"Function must be T-to-unsigned int");
Edit: I wrote a small example to convince myself i got the assert right, might as well post it. Here, using A will compile OK but B will fail the assertion.
#include <type_traits>
template<class T, class F>
class SortedContainer {
static_assert(std::is_same<unsigned int,
typename std::result_of<F(T)>::type>::value,
"Function must be T-to-unsigned int");
};
struct A {
unsigned int operator()(double) { return 0; }
};
struct B {
double operator()(double) { return 0; }
};
int main() {
SortedContainer<double, A> a;
SortedContainer<double, B> b;
}
Based on your other edit:
Note that the templated type F only captures the type of the function. You still need an object of this type - the actual function - to call. Again, compare with std::map which first is templated to take a comparator type, and then has a constructor that takes an object of this type. This is true even if you use a normal function - the type will be SortedContainer<T, unsigned int (*)(T)>, but you would somehow need to pass the actual function pointer into the container (probably through the constructor).
Something like this:
template<class T, class F>
class SortedContainer {
public:
SortedContainer(F f = F()): func(f) {}
void foo() {
// ...
func();
// ...
}
private:
F func;
};
struct A {
unsigned int operator()() { return 0; }
};
int main() {
A a;
SortedContainer<double, A> c(a);
c.foo();
}
IMO, you don't require a separate template argument for Function F.
template<typename T> // F not required!
class SortedContainer {
...
};
Choose a good name and use that function by overloading it for various cases. e.g. to_uint()
Since you want to map (i.e. relate) a type to an unsigned int (uint), use following function in global scope:
template<typename T>
uint to_uint (const T& t) {
return t.to_uint(); // Must have `uint to_uint() const` member, else error
}
// Overloads of `to_uint()` for PODs (if needed)
template<typename T> // For all kinds of pointers
uint to_uint (const T* const pT) {
if(pT == nullptr)
<error handling>;
return to_uint(*pT);
}
Scenario: For Sorted_Container<X>, whenever to_uint(x) is invoked, then:
If X is a class, then it must have uint to_uint() const method
Else if X is a POD, then you may have to overload to_uint() for that type
Else, the compiler will generate an error
It's as you said, pretty much:
template< typename T, unsigned int f(T) >
struct SortedContainer;
...
SortedContainer<Package, weight> sc;
if you actually wanted the argument to be a function pointer rather than a function,
template< typename T, unsigned int (*f)(T) >
and similarly if you want the argument to be a function reference.
(naturally, this will only work for dumb functions, not for function objects with an operator() operator of the right signature)
You may use C-style function pointers as #Hurkyl suggests, or std::function which probably can't be template parameters, but I think that idea is wrong.
C++ templates are duck-typed, so STL code in many places (std::unordered_map -> std::hash, std::sort -> std::less) relies on that. I think you should also apply this approach - just ask user to provide specialization for type T:
/* Universal implementation */
template<typename T>
unsigned int sorted_container_weight(T t) { return t; }
template<typename T>
class SortedContainer {
T t;
public:
unsigned int somefunc() {
return sorted_container_weight(t);
}
};
template<>
unsigned int sorted_container_weight<Package>(Package p) { return p.w; }
SortedContainer<Package> sc;
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.
I want to do something like the following:
Example(&Class::MemberFunction, this));
//...
template<class T_CLASS>
inline static void Example(void (T_CLASS::*MemberFunctionPointer)(), T_CLASS* InstancePointer)
{
SomeClass<T_CLASS>::Bind<MemberFunctionPointer>(InstancePointer);
}
But I get the error: *template parameter 'T_MEMBER_FUNCTION' : 'MemberFunctionPointer' : a local variable cannot be used as a non-type argument*
Any solutions for this problem? I want to provide an easier way to call "Bind"
Thanks, Mirco
//edit:
I want MemberFunctionPointer to be a non-type template parameter because in "Bind" I again need it as a template argument.
As you wrote in your answers, in my case MemberFunctionPointer is a variable and its value is unknown at compile time. But MemberFunctionPointer always points to the same function. Is there a way to for example make it constant so that the compiler knows it at compile time?
There are two kinds of things template parameters can be: types and compile-time constant expressions. The contents of a function parameter is not a compile-time determinable value. And therefore, the compiler cannot instantiate a template based on it.
Remember: a template is a type. And types must be determinable at compile time.
You probably should pass the member pointer as an argument to the Bind function.
I am not quite sure what you are trying to achieve?
If a MemberFunctionPointer is a variable, that the value of is unknown at compile time and, for example, may depend on some user behaviour - then it cannot be used as a template argument.
If, on the other hand, MemberFunctionPointer can be actually deduced at compile-time, you should pass it as a template argument, instead of a function parameter. Consider the following example:
(use Bind and call in the first case; in the second case, use StaticBind and callStatic)
#include <stdio.h>
class X {
public:
int x;
void foo() {printf("foo\n");}
void bar() {printf("bar\n");}
};
template <typename T>
class SomeClass {
public:
static void Bind(void (T::*MemberFunctionPointer)(), T *obj) {
(obj->*MemberFunctionPointer)();
}
template <void (T::*MemberFunctionPointer)()>
static void StaticBind(T *obj) {
(obj->*MemberFunctionPointer)();
}
};
template <class C>
static inline void call(void (C::*MemberFunctionPointer)(), C *obj) {
SomeClass<C>::Bind(MemberFunctionPointer,obj);
}
template <class C, void (C::*MemberFunctionPointer)()>
static inline void callStatic(C *obj) {
SomeClass<C>::template StaticBind<MemberFunctionPointer>(obj);
}
int main() {
X obj;
call<X>(&X::foo,&obj);
callStatic<X,&X::bar>(&obj);
return 0;
}
Template parameters have to be known at compile-time. The contents of a pointer variable that is a function's parameter depends on how this function is invoked. This is not known at compile-time!
If you know this pointer at compile-time already, you can turn the function pointer runtime parameter into a template parameter:
template<class T_CLASS, void(T_CLASS::*MemFunPtr)()>
void Example(T_CLASS* InstancePointer) {...}
Here, MemFunPtr is a template parameter that is known at compile-time and can thus be resused as a template parameter for another function or class template...
MemberFunctionPointer is a variable not a type (or compile-time constant), hence cannot be used, what you need is the real signature of that function, something like this may be better..
template<typename T_FUNC_PTR, class T_CLASS>
inline static void Example(T_FUNC_PTR fPtr, T_CLASS* InstancePointer)
{
SomeClass<T_CLASS>::Bind<T_FUNC_PTR>(fPtr, InstancePointer);
}
i.e. let the compiler deduce the type of the function pointer (NOTE: you will have to propagate the pointer to the function too), to call
Example(&foo::bar, foo_inst);
This is untested and off the top of my head, so the syntax could be slightly off...
EDIT: here is a simpler example to demonstrate the concept:
#include <iostream>
struct foo
{
void bar() { std::cout << "foo::bar()" << std::endl; }
};
template<typename T_FUNC_PTR, typename T_CLASS>
void exec(T_FUNC_PTR ptr, T_CLASS& inst)
{
(inst.*ptr)();
}
int main(void)
{
foo inst;
exec(&foo::bar, inst);
}
I want to detect existence of a specific member function for a class, using the usual SFINAE trick.
template<typename T>
struct has_alloc
{
template<typename U,U x>
struct dummy;
template<typename U>
static char test(dummy<void* (U::*)(std::size_t),&U::allocate>*);
template<typename U>
static char (&test(...))[2];
static bool const value = sizeof(test<T>(0)) ==1;
};
It should be noted that this detects a different kind of allocator which has void* allocate(std::size_t) as member function which are non standard (probably some raw memory allocator).
Next, I have an incomplete type and an std::allocator for that incomplete type.
struct test;
typedef std::allocator<test> test_alloc;
And I am checking whether the test_alloc is the one I am looking for.
struct kind_of_alloc
{
const static bool value = has_alloc<test_alloc>::value;
};
Surely struct test will be complete when I will "use" test_alloc such as
#include "test_def.hpp"//contains the code posted above
struct test{};
void use()
{
test_alloc a;
}
in another compilation unit. However when the has_alloc test happens,the compiler tries to instantiate the allocate function for std::allocator and finds that sizeof an incomplete type is used inside function body, and causes a hard error.
It seems that the error doesn't occur if the implementation of allocate separated and included separately at the point of use such as
template<typename T>
T* alloc<T>::allocate(std::size_t n)
{
return (T*)operator new(sizeof(T)*n);
}
void use()
{
test_alloc a;
a.allocate(2);
}
And test_def.hpp contains
template<typename T>
struct alloc
{
T* allocate(std::size_t n);
};
However while I can do this for alloc<T> , it is not possible for std::allocator as separating out the implementation is not possible.
What I am looking for is it possible to test whether a function with void* allocate(size_t) exists in test_alloc. If not, it will test negative, and if yes ,i.e. if the function signature matches, even if it can not be instantiated there, test positive.
No, SFINAE is only in effect during overload resolution. Once a resolution has been made and the compiler begins instantiating the function SFINAE is over.
Edit: and taking the address of a function instantiates it.