I'm writing some generic software using concepts and I want to check if a particular function symbol name exists with a signature of (void)(int,int) on a struct. To do this I've I'm thinking of approaching this problem through template specialization but I'm sort of lost.
What I want is something to work and through compile time errors if the concept is not satisfied like so:
struct TypeA {
// Passes concept
void process (int a ,int b) const {};
};
struct TypeB {
// Does not pass concept
void process (float a) const {};
};
struct TestConcepts {
/* concept code here */
TestConcepts(T t) {
process_concept(t.process);
};
};
int main(void) {
// Should pass
TestConcept(TypeA{});
// Should throw error
TestConcept(TypeB{});
return 0;
}
I'm having a hard time filling in the blanks but this is what I have so far:
struct TestConcepts {
/* concept code here */
struct process_concept {
process_concept((V*)(IA,IB)){
if (is_integral<IA>::value && is_integral<IB>::value && is_same<V, void>) {
return;
}
static_assert(false, "You must provide function called process of type (void)(int,int)");
};
};
TestConcepts(T t) {
process_concept(&t.process);
};
};
Unfortunately this doesn't work. How can I get this function signature correct?
How about using a function that returns a declared function pointer?
struct TypeA {
// Passes concept
void process (int a ,int b) const {};
};
struct TypeB {
// Does not pass concept
void process (float a) const {};
};
template<typename T>
auto TestConcepts(T) -> void(T::*)(int, int) const
{
return &T::process;
}
int main(void) {
// Should pass
TestConcepts(TypeA{});
// Should throw error
TestConcepts(TypeB{});
return 0;
}
Output:
Error(s):
source_file.cpp: In instantiation of ‘void (T::* TestConcepts(T))(int, int) const [with T = TypeB]’:
source_file.cpp:26:23: required from here
source_file.cpp:19:16: error: cannot convert ‘void (TypeB::*)(float) const’ to ‘void (TypeB::*)(int, int) const’ in return
return &T::process;
^
EDIT: more options
If you want to include void process(long int a, long int b) const; or void process(int a, int b, int c=0) const;, like aschepler is suggesting, you can use type traits.
struct TypeA {
// Passes concept
void process(int a, int b) const {};
};
struct TypeB {
// Does not pass concept
void process(float a) const {};
};
struct TypeC {
// Passes concept
void process(long int a, long int b) const {};
};
struct TypeD {
// Passes concept
void process(int a, int b, int c = 0) const {};
};
struct TypeE {
// Does not pass concept
void process(int a, int b, int c) const {};
};
#include <type_traits>
template<typename T, typename A1, typename A2, typename... An>
typename std::enable_if<
std::is_integral<A1>::value &&
std::is_integral<A2>::value
>::type
TestProcess(const T& t, void(T::*)(A1, A2, An...) const) {
t.process(1, 2);
};
template<typename T>
void TestConcepts(const T& t)
{
TestProcess(t, &T::process);
}
int main(void) {
// Passes
TestConcepts(TypeA{});
// Throws compilation error
TestConcepts(TypeB{});
// Passes
TestConcepts(TypeC{});
// Passes
TestConcepts(TypeD{});
// Throws compilation error
TestConcepts(TypeE{});
return 0;
}
Related
I have the following template class where I want to cycle through the template types and do something for each type. This is a simplified example.
template<class... T>
class Handler
{
private:
template<class A>
void Do1_(int a, int b)
{
A obj{};
obj.Process(a, b);
}
template<class A, class... B>
void Do_(int a, int b)
{
Do1_<A>(a, b);
Do_<B...>(a, b);
}
public:
void Do(int a, int b)
{
Do_<T...>(a, b);
}
};
struct Foo1 {
void Process(int a, int b) {}
};
struct Foo2 {
void Process(int a, int b) {}
};
class Bar : public Handler<Foo1, Foo2> {};
But the expansion for Do_<B...> gives me trouble, I get compilation error about "could not deduce template argument for 'A'", when compiling Do_<B...>. What should be the correct expansion be here in order to make it compile, if possible.
You are over-complicating matters. This should do (no pun intended):
template<class... T>
class Handler
{
public:
void Do(int a, int b) {
int dummy[] = {(T{}.Process(a, b), 0)...};
}
};
Demo
Can we chose which function template overload should be used in this case?
struct X { };
struct A { A(X){} };
struct B { B(X){} };
template<class T>
void fun(T, A) { }
template<class T>
void fun(T, B) { }
int main() {
/* explicitly choose overload */ fun(1, X());
}
Error:
error: call of overloaded 'fun(int, X)' is ambiguous
/* explicitly choose overload */ fun(1, X());
^
candidate: void fun(T, A) [with T = int]
void fun(T, A) { }
^~~
candidate: void fun(T, B) [with T = int]
void fun(T, B) { }
^~~
For normal function it looks like this:
void fun(A){}
void fun(B){}
int main() {
((void(*)(A))(fun))(X());
}
Is it possible?
Improving your example, you can try with
((void(*)(int, A))(fun))(1, X());
If you choose not to explicitly specify the first parameter type but still want to specify the second you could go along with lambda dedicated for casting purpose (c++14 solution):
struct X { };
struct A { A(X){} };
struct B { B(X){} };
template<class T>
void fun(T, A) { }
template<class T>
void fun(T, B) { }
int main() {
[](auto v, auto x){ static_cast<void(*)(decltype(v), A)>(fun)(v, x); }(1, X());
}
[live demo]
A low-tech solution to this problem would be to add one extra level of indirection. Add a function like funA, whose sole purpose is to give an explicit name to the first version of fun:
struct X { };
struct A { A(X){} };
struct B { B(X){} };
template<class T>
void fun(T, A) { }
template<class T>
void fun(T, B) { }
template <class T>
void funA(T t, A a) { fun(t, a); }
int main() {
/* explicitly choose overload */ funA(1, X());
}
However, I wonder why you cannot just change the argument to A(X()). You will have to change the calling code anyway, so what's the problem?
I'm working on a property-like system in c++, one component of which in an accessor template class, which creates static functions I can then pass pointers of, into my property class for get an set operations.
Here's what the accessor looks like:
template <typename T, typename V, V (T::*getf)(), void (T::*setf)(V)>
struct Accessor
{
static V Get(T* obj)
{
return (obj->*getf)();
}
static void Set(T* obj, V aValue)
{
return (obj->*setf)(aValue);
}
};
And a test class that I'll be using the accessor on. Note how SetFoo takes an int, where SetBar takes a const int&.
class TargetClass
{
int foo;
int bar;
public:
TargetClass(int f, int b)
: foo(f)
, bar(b)
{
}
int GetFoo()
{
return foo;
}
void SetFoo(int f)
{
foo = f;
}
int GetBar()
{
return bar;
}
void SetBar(const int& b)
{
bar = b;
}
};
And finally, here's the usage:
int main()
{
TargetClass* target = new TargetClass(5, 3);
// Works great
typedef Accessor<TargetClass, int, &TargetClass::GetFoo, &TargetClass::SetFoo> fooAcessor;
fooAcessor::Set(target, 13);
int foo = fooAcessor::Get(target);
// Doesn't work, because TargetClass::SetBar takes a const int& as an argument, instead of an int
typedef Accessor<TargetClass, int, &TargetClass::GetBar, &TargetClass::SetBar> barAcessor;
delete target;
return 0;
}
I've tried "overloading" the Acessor struct so it's 4th argument is a const V&, but that doesn't seem to work. Is what I'm trying to do here possible?
If you have access to c++17, and assuming that the proposed standard doesn't change in the meantime, you can have the types deduced automatically:
template <typename F>
struct get_types;
template <typename T, typename V, typename... VP>
struct get_types<V(T::*)(VP...)>
{
using return_type = V;
using class_type = T;
};
template <auto getf, auto setf>
struct Accessor {
using V = typename get_types<decltype(getf)>::return_type;
using T = typename get_types<decltype(getf)>::class_type;
static V Get(T* obj)
{
return (obj->*getf)();
}
static void Set(T* obj, V aValue)
{
return (obj->*setf)(aValue);
}
};
class TargetClass {
int foo;
int bar;
public:
TargetClass(int f, int b)
: foo(f)
, bar(b) {}
int GetFoo() { return foo; }
void SetFoo(int f) { foo = f; }
int GetBar() { return bar; }
void SetBar(const int& b) { bar = b; }
};
int main()
{
TargetClass* target = new TargetClass(5, 3);
using fooAccessor = Accessor<&TargetClass::GetFoo, &TargetClass::SetFoo>;
fooAccessor::Set(target, 13);
int foo = fooAccessor::Get(target);
using barAcessor = Accessor<&TargetClass::GetBar, &TargetClass::SetBar>;
barAcessor::Set(target, 13);
int bar = barAcessor::Get(target);
delete target;
return foo + bar;
}
As of this writing the program above compiles on gcc 7 (snapshot).
You could add another template parameter for the set function's parameter:
template <typename T, typename V, typename VP, V(T::*getf)(), void (T::*setf)(VP)>
And then use for both cases:
typedef Accessor<TargetClass, int, int,
&TargetClass::GetFoo, &TargetClass::SetFoo> fooAcessor;
typedef Accessor<TargetClass, int, const int&,
&TargetClass::GetBar, &TargetClass::SetBar> barAcessor;
Full program:
template <typename T, typename V, typename VP, V(T::*getf)(), void (T::*setf)(VP)>
struct Accessor
{
static V Get(T* obj)
{
return (obj->*getf)();
}
static void Set(T* obj, V aValue)
{
return (obj->*setf)(aValue);
}
};
class TargetClass
{
int foo;
int bar;
public:
TargetClass(int f, int b)
: foo(f)
, bar(b)
{
}
int GetFoo()
{
return foo;
}
void SetFoo(int f)
{
foo = f;
}
int GetBar()
{
return bar;
}
void SetBar(const int& b)
{
bar = b;
}
};
int main()
{
TargetClass* target = new TargetClass(5, 3);
// now has extra int parameter
typedef Accessor<TargetClass, int, int, &TargetClass::GetFoo, &TargetClass::SetFoo> fooAcessor;
fooAcessor::Set(target, 13);
int foo = fooAcessor::Get(target);
// Works now, because TargetClass::SetBar's function parameter was specified explicitly as const int&
typedef Accessor<TargetClass, int, const int&, &TargetClass::GetBar, &TargetClass::SetBar> barAcessor;
barAcessor::Set(target, 13);
int bar = barAcessor::Get(target);
delete target;
return 0;
}
Suppose we have a class:
template <class Type>
class A
{
public:
void function1(float a, Type b);
void function1(float a, float b);
};
Now instantiate the class like this:
A<int> a;
It's fine, this class will have 2 overloaded functions with these parameters: (float a, int b); (float a, float b);
But when you instantiate the class like this:
A<float> a;
You get compile error:
member function redeclared.
So, depending on the type of Type, I wan't (or don't want) the compiler to define a function, something like this:
template <class Type>
class A
{
public:
void function1(float a, Type b);
#if Type != float
void function1(float a, float b);
#endif
};
But, of course, the syntax above doesn't work. Is it possible to perform such a task in C++? If possible, please provide an example.
You can use some C++11 std::enable_if :
template <class Type>
class A
{
public:
template<typename t = Type,
typename std::enable_if<!std::is_same<t, float>::value, int>::type = 0>
void function1(float a, Type b) {
}
void function1(float a, float b) {
}
};
You could use template specialization:
template <class Type>
class A {
public:
void function1(float a, Type b) {
}
void function1(float a, float b) {
}
};
template <>
class A<float> {
public:
void function1(float a, float b) {
}
};
// ...
A<int> a_int;
a_int.function1(23.4f, 1);
a_int.function1(23.4f, 56.7f);
A<float> a_float;
a_float.function1(23.4f, 56.7f);
--- EDIT ---
If you have a large number of common functions, you could do something like this:
class AImp {
public:
void function1(float a, float b) {
}
void function1(float a, double b) {
}
void function1(float a, const std::string& b) {
}
// Other functions...
};
template <class Type>
class A : public AImp {
public:
void function1(float a, Type b) {
}
using AImp::function1;
};
template <>
class A<float> : public AImp {
};
// ...
A<int> a_int;
a_int.function1(23.4f, 1);
a_int.function1(23.4f, 56.7f);
a_int.function1(23.4f, 56.7);
a_int.function1(23.4f, "bar");
A<float> a_float;
a_float.function1(23.4f, 56.7f);
a_float.function1(23.4f, 56.7);
a_float.function1(23.4f, "bar");
Use SFINAE:
#include <iostream>
#include <type_traits>
template <typename Type>
struct Foo {
template <typename T = Type>
void function1(float a, float b, typename std::enable_if<!std::is_same<T, float>::value>::type *c = 0) {
std::cout << "float\n";
}
void function1(float a, Type b) {
std::cout << "type\n";
}
};
int main() {
Foo<float> f;
f.function1(1, 1);
f.function1(1.0f,1.0f);
Foo<int> g;
g.function1(1,1);
g.function1(1.0f,1.0f);
g.function1(1.0,1.0); // warning!
}
Output:
type
type
type
float
type
You'll need C++11 mode, to allow the default template parameter in a function template. And also to get enable_if and is_same, although you could get enable_if from Boost instead.
The "warning!" is because with your original code g.function1(1.0,1.0); was ambiguous. Now the the non-template overload is preferred. You can make it ambiguous again by doing
template <typename T = Type>
void function1(float a, Type b, typename std::enable_if<true>::type *c = 0) {
std::cout << "type\n";
}
The following code (condensed from a larger program) does not compile with clang or gcc.
struct S1 {
void m1() {}
};
template<typename B> struct S2 : B {
void m2() {}
void m3();
};
template<typename S, void (S::*m)()> void f1(S* o) {
(o->*m)();
}
template<typename B> void S2<B>::m3() {
f1<S2, &S2::m1>(this);
}
int main() {
void (S2<S1>::*m)() = &S2<S1>::m1;
S2<S1> o;
o.m3();
}
Here is clang's error message:
bad.cc:15:3: error: no matching function for call to 'f1'
f1<S2, &S2::m1>(this);
^~~~~~~~~~~~~~~
bad.cc:21:5: note: in instantiation of member function 'S2<S1>::m3' requested
here
o.m3();
^
bad.cc:10:43: note: candidate template ignored: invalid explicitly-specified
argument for template parameter 'm'
template<typename S, void (S::*m)()> void f1(S* o) {
^
1 error generated.
This code compiles when I replace m1 by m2. Clearly the compiler knows about m1 (different message when I replace m1 by m4), so why should a pointer to it be invalid in this context?
The thing is, the type of m1 is void(S1::*)(void), not void(S2::*)(void). So fix it by leveraging the known base class name:
struct S1 {
void m1() {}
};
template<typename B> struct S2 : B {
void m2() {}
void m3();
};
template<typename S, typename B, void (B::*m)(void)> void f1(S* o) {
(o->*m)();
}
template<typename B> void S2<B>::m3() {
f1<S2, B, &B::m1>(this);
}
int main() {
S2<S1> o;
o.m3();
}
Of course this doesn't (yet) scale to methods defined in indirect base classes, but with a bit of TMP it can be done (will see if I can post that while the intermission of Going Native 2012 lasts :))
The more 'flexible' approach would be:
template<typename B, typename MF> void f1(B* o, MF mfp) {
(o->*mfp)();
}
template<typename B> void S2<B>::m3() {
f1(this, &B::m1);
}
You could/should use typetraits to ensure that S2<B>& is convertible to a B& if the class layout doesn't already explicitly guarantee that, as in your current example.