Specializing struct template using enable_if - c++

I'm trying to create a template class, that will implement a callback with a different signature, depending on if it is instantiated with one type, or two.
struct NoIntermediate
{
};
template<typename R, typename I>
struct ParserCallbackSwitch
{
using type = std::function<bool(const std::string &, R&, I&)>;
}
template<typename R, typename I = NoIntermediate>
class OtherClass
{
public:
typedef ParserCallbackSwitch<R, I>::type ParserType;
}
Now I want to add code so that if I is not specified when instantiating 'OtherClass', that ParserCallbackSwitch would be:
template<typename R, typename I>
struct ParserCallbackSwitch
{
using type = std::function<bool(const std::string &, R&)>;
}
Notice that in this case ParserCallbackSwitch::type is a function with only two parameters.
I wanted to be able to do the following:
OtherClass<int, float> p; // p::ParserType = std::function<bool(std::string &, int &, float &);
OtherClass<int> q; // q::ParserType = std::function<bool(std::string &, int &);
I can't figure out how to partially specify ParserCallbackSwitch for the case when I is of type NoIntermediate (i.e. I is not specified)
SOLUTION: Based on response below. Here is the code I finally ended up using.
struct NoIntermediate {};
template<typename R, typename I = NoIntermediate>
struct ParserCallbackSwitch
{
using type = std::function<bool(const std::string &, R&, I&)>;
};
template<typename R>
struct ParserCallbackSwitch<R, NoIntermediate>
{
using type = std::function<bool(const std::string &, R&)>;
};
template<typename R, typename I = NoIntermediate>
class OtherClass
{
public:
typedef ParserCallbackSwitch<R, I>::type ParserType;
}

So! You're not properly specializing a template. You're defining two unrelated class templates that happen to have the same name.
There are multiple ways to do what you propose. This one gives the least specialized template a parameter pack.
#include <functional>
#include <type_traits>
template<typename... S>
struct OtherClass;
template<typename R, typename I>
struct OtherClass<R, I> {
using ParserType = std::function<bool(std::string&, R&, I&)>;
};
template<typename R>
struct OtherClass<R> {
using ParserType = std::function<bool(std::string&, R&)>;
};
int main(void) {
static_assert(std::is_same<OtherClass<int, float>::ParserType,
std::function<bool(std::string&, int&,
float&)>>(),
"Something wrong here.");
static_assert(std::is_same<OtherClass<int>::ParserType,
std::function<bool(std::string&, int&)>>(),
"Hmmmmmm.");
return 0;
}
Your idea of using a default type in a parameter also works, but your syntax was slightly off. Here's how that would look.
#include <functional>
#include <type_traits>
template<typename R, typename I = void>
struct OtherClass {
using ParserType = std::function<bool(std::string&, R&, I&)>;
};
template<typename R>
struct OtherClass<R, void> {
using ParserType = std::function<bool(std::string&, R&)>;
};
int main(void) {
static_assert(std::is_same<OtherClass<int, float>::ParserType,
std::function<bool(std::string&, int&,
float&)>>(),
"Something wrong here.");
static_assert(std::is_same<OtherClass<int>::ParserType,
std::function<bool(std::string&, int&)>>(),
"Hmmmmmm.");
return 0;
}

Related

Is it possible to call a function with all arguments default constructed?

Is it possible to have a function like std::invoke, but this function calls all arguments of the given function automatically with the default constructed types?
#include <iostream>
#include <functional>
// e.g. for a single arg
struct Test{
void operator()(int i) {
std::cout << std::to_string(i) << "\n";
}
};
int main(){
Test test;
std::invoke(test, {}); // this doesn't work, would like it to call with default constructed int (0).
return 0;
}
I would like something like
int main()
{
Test test;
invoke_with_defaults(test); // prints 0
return 0;
}
You need a class with a templated conversion operator, returning {} for any type:
struct DefaultConstruct
{
DefaultConstruct() = default;
DefaultConstruct(const DefaultConstruct &) = delete;
DefaultConstruct &operator=(const DefaultConstruct &) = delete;
template <typename T> operator T() && {return {};}
};
int main()
{
Test test;
std::invoke(test, DefaultConstruct{});
}
It's then possible to write a template that automatically determines how many of those have to be passed:
template <typename F, typename ...P>
decltype(auto) InvokeDefault(F &&func)
{
if constexpr (std::is_invocable_v<F, P...>)
return std::invoke(std::forward<F>(func), P{}...);
else
return InvokeDefault<F, P..., DefaultConstruct>(std::forward<F>(func));
}
int main()
{
Test test;
InvokeDefault(test);
}
And if the argument isn't callable at all, you get a compilation error after exceeding some implementation-defined limit (on Clang I got up to 256).
Initializer lists like {} cannot be forwarded as a parameter due not work due to language restrictions.
But you can mimick {} by wrapping it into a Defaulter class which can be passed around:
#include <iostream>
#include <functional>
// e.g. for a single arg
struct Test{
void operator()(int i) {
std::cout << std::to_string(i) << "\n";
}
};
struct Defaulter{
template<typename T>
operator T(){
return {};
}
};
int main(){
Test test;
std::invoke(test, Defaulter{});
return 0;
}
You could use something like this to create a tuple of all of the argument types, and then pass a default constructed instance of it to std::apply. The specialisation list would need to be quite long though to cover all of the const, volatile, noexcept, and ref-qualified variants though, and of course it cannot work with template or overloaded functions.
Eg:
template <typename T>
struct arg_extractor : arg_extractor<decltype(&T::operator())> {
};
template <typename R, typename... Args>
struct arg_extractor<R (*)(Args...)> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...)> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...) const> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...) noexcept> {
using type = std::tuple<R, Args...>;
};
template <typename R, typename C, typename... Args>
struct arg_extractor<R (C::*)(Args...) const noexcept> {
using type = std::tuple<R, Args...>;
};
// All the rest...
template <typename T>
using arg_extractor_t = typename arg_extractor<T>::type;

Converting a pointer-to-member type to a simple pointer type

I have the following type, which I get with decltype
QString UserInfo::*&
I can remove the & part by wrapping decltype with std::remove_reference_t but I also want to remove UserInfo::* part
How can I do that so I can use only QString type in my templates
I'm using this template in initializer list where I don't have access to solid object or this pointer to .* operator in decltype
Using a valid object is not necessary in unevaluated contexts (like decltype). To exaggerate a little, you could even dereference a null pointer in there, and nothing bad would happen, since the dereference is never actually evaluated.
To create an object of a type that is not valid, but can be used in unevaluated contexts, you can use std::declval.
template<class T>
using member_type = decltype(std::declval<UserInfo>().*std::declval<T>());
Use template specialization to extract member type:
template <typename MemberType>
struct member_type;
template <typename Class, typename Member>
struct member_type<Member Class::*>
{
using type = Member;
};
template<typename T>
using member_type_t = typename member_type<T>::type;
class A
{
public:
int b;
};
using t = member_type_t<decltype(&A::b)>;
You can write a trait to extract the type of the member from the member function pointer:
#include <type_traits>
template <typename T>
struct member_type;
template <typename C, typename T>
struct member_type<T C::*> {
using type = T;
};
template <typename T>
using member_type_t = typename member_type<T>::type;
struct foo {
int bar;
};
int main()
{
int foo::*ptr = &foo::bar;
using T = member_type_t<decltype(ptr)>;
static_assert( std::is_same_v<int,T>);
}
Here is my version using C++17 in such way that decltype is not needed when template is used (a bit more handy):
#include <iostream>
#include <string>
#include <type_traits>
template <auto Fp>
struct field_type;
template<typename R, typename T, R (T::*FP)>
struct field_type<FP>
{
using type = R;
};
template<typename R, typename T, typename...Args, R (T::*FP)(Args...)>
struct field_type<FP>
{
using type = R;
};
template<auto FP>
using field_type_t = typename field_type<FP>::type;
class Foo {
public:
int x = 0;
double y = 0;
std::string s;
const int cx = 0;
Foo() = default;
void bar() {
std::cout << "bar\n";
}
int par(int z) {
std::cout << "bar\n";
return z;
}
};
template<auto F, typename T>
constexpr bool test = std::is_same_v<field_type_t<F>, T>;
static_assert(test<&Foo::x, int>, "");
static_assert(test<&Foo::cx, const int>, "");
static_assert(test<&Foo::s, std::string>, "");
static_assert(test<&Foo::y, double>, "");
#ifndef HIDE_PROBLEM_ON_GCC_11
static_assert(test<&Foo::bar, void>, "");
static_assert(test<&Foo::par, int>, "");
#endif
For some strange reason works on all compilers, but not for gcc 11.1 or newer.
https://godbolt.org/z/4e7oKbod1

How get the class (object type) from pointer to method

I have a pointer to the method:
struct A { int method() { return 0; } };
auto fn = &A::method;
I can get a return type by std::result_of, but how I can get from fn the class owner of the method?
You can match it using class-template-specialization:
//Primary template
template<typename T> struct ClassOf {};
//Thanks T.C for suggesting leaving out the funtion /^argument
template<typename Return, typename Class>
struct ClassOf<Return (Class::*)>{ using type = Class; };
//An alias
template< typename T> using ClassOf_t = typename ClassOf<T>::type;
Hence given:
struct A { int method() { return 0; } };
auto fn = &A::method;
We can retrieve the class like:
ClassOf_t<decltype(fn)> a;
Full example Here.
Try this:
template<class T>
struct MethodInfo;
template<class C, class R, class... A>
struct MethodInfo<R(C::*)(A...)> //method pointer
{
typedef C ClassType;
typedef R ReturnType;
typedef std::tuple<A...> ArgsTuple;
};
template<class C, class R, class... A>
struct MethodInfo<R(C::*)(A...) const> : MethodInfo<R(C::*)(A...)> {}; //const method pointer
template<class C, class R, class... A>
struct MethodInfo<R(C::*)(A...) volatile> : MethodInfo<R(C::*)(A...)> {}; //volatile method pointer
Boost callable traits answer, I like it better than shorter answers here since it is a bit more readable to me, but opinions might differ...
#include<string>
#include<type_traits>
#include<tuple>
#include <boost/callable_traits/args.hpp>
struct S{
int val=46;
int get(){
return val;
}
void method(const std::string ){
}
};
int main(){
using Ts1 = boost::callable_traits::args_t<decltype(&S::val)>;
using Ts2 = boost::callable_traits::args_t<decltype(&S::get)>;
using Ts3 = boost::callable_traits::args_t<decltype(&S::method)>;
std::remove_cvref_t<std::tuple_element<0,Ts1>::type> s1;
s1.val++;
std::remove_cvref_t<std::tuple_element<0,Ts2>::type> s2;
s2.val++;
std::remove_cvref_t<std::tuple_element<0,Ts3>::type> s3;
s3.val++;
}
s1, s2, s3 are all of type S.
Obviously you need to do the logic only once, I did it 3 times to show it works fine for pointer to member, pointer to function that takes 0 arguments, pointer to function that takes 1 argument.

Access type member

In my example I have a class Foo<T>. In my function test I need to get the template parameter of Foo otherwise the normal type. First I started to use std::conditional but forgot that the template parameters must all be valid, no matter which one is picked. Is the only way to create a type-specialisation for non-Foo types?
Example
#include <type_traits>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template <typename T>
void test(const T& a)
{
// actually I would have used !is_foo<T>::value for the first arg
// but this check is fine to minimise the example
using MY_TYPE = typename std::conditional<
std::is_same<T, int>::value,
T,
typename T::M>::type; // <---Error: error: type 'int' cannot be used prior to '::' because it has no members
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
Well you could make an UnFoo helper to get the right type for you:
template <typename T>
struct UnFoo {
using type = T;
};
template <typename T>
struct UnFoo<Foo<T>> {
using type = T;
};
template <typename T>
void test(const T& a)
{
using MY_TYPE = typename UnFoo<T>::type; //maybe with a helper to get rid of typename
}
Another option would be to write an overload for Foo<T> and have it delegate to the other function, but that depends on what your real test function does.
You can do some void_t magic to allow SFINAE to figure help you out:
#include <type_traits>
#include <iostream>
#include <typeinfo>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
// primary template handles types that have no nested ::T member:
template< class T, class = void_t<> >
struct M_or_T { using type = T; };
// specialization recognizes types that do have a nested ::T member:
template< class T >
struct M_or_T<T, void_t<typename T::M>> { using type = typename T::M; };
template <typename T>
void test(const T& a)
{
using MY_TYPE = typename M_or_T<T>::type;
std::cout << typeid(MY_TYPE).name() << "\n";
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
What happens is that the second overload of M_or_T substitution fails for int (and for any type without a type member M) and thus the first overload is chosen. For types which have a type member M, a more specialized second overload is chosen.
#include <type_traits>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template <typename T>
void test(const Foo<T>& a)
{
using MY_TYPE = Foo<T>::M;
testOther<MY_TYPE>(a);
}
template <typename T>
void test(const T& a)
{
using MY_TYPE = T;
testOther<MY_TYPE>(a);
}
template <typename T, typename S>
void testOther(const S& a)
{
// do stuff
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
I'm not exactly sure what you wanted, but I hope this is what you wanted. It might be a bit off. I didn't compile this.

How to implement is_polymorphic_functor?

I'm trying to implement is_polymorphic_functor meta-function to get the following results:
//non-polymorphic functor
template<typename T> struct X { void operator()(T); };
//polymorphic functor
struct Y { template<typename T> void operator()(T); };
std::cout << is_polymorphic_functor<X<int>>::value << std::endl; //false
std::cout << is_polymorphic_functor<Y>::value << std::endl; //true
Well that is just an example. Ideally, it should work for any number of parameters, i.e operator()(T...). Here are few more test cases which I used to test #Andrei Tita's solution which fails for two test-cases.
And I tried this:
template<typename F>
struct is_polymorphic_functor
{
private:
typedef struct { char x[1]; } yes;
typedef struct { char x[10]; } no;
static yes check(...);
template<typename T >
static no check(T*, char (*) [sizeof(functor_traits<T>)] = 0 );
public:
static const bool value = sizeof(check(static_cast<F*>(0))) == sizeof(yes);
};
which attempts to make use of the following implementation of functor_traits:
//functor traits
template <typename T>
struct functor_traits : functor_traits<decltype(&T::operator())>{};
template <typename C, typename R, typename... A>
struct functor_traits<R(C::*)(A...) const> : functor_traits<R(C::*)(A...)>{};
template <typename C, typename R, typename... A>
struct functor_traits<R(C::*)(A...)>
{
static const size_t arity = sizeof...(A) };
typedef R result_type;
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<A...>>::type type;
};
};
which gives the following error for polymorphic functors:
error: decltype cannot resolve address of overloaded function
How to fix this issue and make is_polymorphic_functor work as expected?
This works for me:
template<typename T>
struct is_polymorphic_functor
{
private:
//test if type U has operator()(V)
template<typename U, typename V>
static auto ftest(U *u, V* v) -> decltype((*u)(*v), char(0));
static std::array<char, 2> ftest(...);
struct private_type { };
public:
static const bool value = sizeof(ftest((T*)nullptr, (private_type*)nullptr)) == 1;
};
Given that the nonpolymorphic functors don't have an overloaded operator():
template<typename T>
class is_polymorphic_functor {
template <typename F, typename = decltype(&F::operator())>
static constexpr bool get(int) { return false; }
template <typename>
static constexpr bool get(...) { return true; }
public:
static constexpr bool value = get<T>(0);
};
template<template<typename>class arbitrary>
struct pathological {
template<typename T>
typename std::enable_if< arbitrary<T>::value >::type operator(T) const {}
};
The above functor is non-polymorphic iff there is exactly one T such that arbitrary<T>::value is true.
It isn't hard to create an template<T> functor which is true on int and possibly double, and only true on double if (arbitrary computation returns 1).
So an uncompromising is_polymorphic is beyond the scope of this universe.
If you don't like the above (because it clearly takes more than just int, other types simply fail to find an overload), we could do this:
template<template<typename>class arbitrary>
struct pathological2 {
void operator()(int) const {}
template<typename T>
typename std::enable_if< arbitrary<T>::value >::type operator(T) const {}
};
where the second "overload" is tested, and if there are no T such that it is taken, then the first overload occurs for every single type.