Reputation: 10468
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?
Upvotes: 2
Views: 96
Reputation: 11261
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;
^
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;
}
Upvotes: 2