Get typename from passed in template - c++

Is it possible to get the typename off a template that is passed into another template? Here is a basic example of what the goal is
#include <memory>
#include <string>
#include <iostream>
class IntId {
private:
int id;
public:
IntId(int id) {
this->id = id;
}
};
class StringId {
private:
std::string id;
public:
StringId(std::string id) {
this->id = id;
}
};
template<typename T_Id>
class Object : public std::enable_shared_from_this<Object<T_Id>>
{
private:
T_Id id;
public:
typedef T_Id T;
Object(T_Id id) {
this->id = id;
}
T_Id getId()
{
return this->id;
}
};
template <class T, class Enable = void>
class Observable {
public:
// Intentionally doesn't have a set so it breaks the build... I want both types to go into the value below
void setNonSpecialized(T value) {
}
};
template<typename T>
class Observable<T, typename std::enable_if<std::is_base_of<Object<IntId>, T>::value>::type> {
private:
std::shared_ptr<T> value;
public:
Observable() {
value = nullptr;
};
void set(std::shared_ptr<T> newValue) {
this->value = newValue;
}
};
class UPCObject : public Object<IntId> {
};
class UserObject : public Object<StringId> {
};
int main()
{
auto upcObject = std::make_shared<UPCObject>();
auto upcObservable = std::make_shared<Observable<UPCObject>>();
upcObservable->set(upcObject); // Expected to succeed as UPCObject inherits from Object<IntId> which matches template
auto userObject = std::make_shared<UserObject>();
auto userObservable = std::make_shared<Observable<UserObject>>();
userObservable->set(userObject); // Want this to succeed as UserObject inherits from Object<StringId> which would match template Object<T::T_Id>
auto intObservable = std::make_shared<Observable<int>>();
intObservable->setNonSpecialized(0); // Expected to succeed and use the value on non-specialized Observable
return 0;
}
In the code above, upcObject succeeds in it's build because it's type matches the templated type. UserObject doesn't because it has a different Id type.
Now if I change the specialization to the following and explicitly describe the type
template <typename T, typename T_Id>
class Observable<T, typename std::enable_if<std::is_base_of<Object<T_Id>, T>::value>::type>
I get the build error 'T_Id': template parameter not used or deducible in partial specialization 'Observable<T,std::enable_if<std::is_base_of<Object<T_Id>,T>::value,void>::type>' because T_Id isn't actually used in Observable at all.
What would be great is if I could do something like the following
template <typename T>
class Observable<T, typename std::enable_if<std::is_base_of<Object<T::T_Id>, T>::value>::type>
Where I am able to get the T_Id off the type being passed in. Because in this specialization I'm checking the base of Object, it should have a type defined on it.

In your case, since you define a typedef in the Object class, you can simply do this:
template <typename T>
class Observable<T, typename std::enable_if<std::is_base_of<Object<typename T::T>, T>::value>::type>
If you were not using that typedef, you could do that:
// returning a pointer protect us from abstract types.
template<typename T>
T* get_object_type(const Object<T>&);
template<typename T>
using object_type_t = typename std::remove_pointer<
decltype(get_object_type(std::declval<const T&>()))
>::type;
And then, use the type trait:
template <typename T>
class Observable<T, typename std::enable_if<std::is_base_of<object_type_t<T>, T>::value>::type>
Note that sfinae will occur first on object_type_t. If the function get_object_type is not callable using a const T&, that specialization of Observable will be ruled out. If T don't extends Object<T>, but the function get_object_type is still callable, then your condition with is_base_of will rule out the specialization of Observable.

Related

Is there an alternative to overloading or template specialization? I am attempting to call a specific function based on a template parameter

I have generated a simpler example of what I am trying to accomplish. I would like to be able to call a function, which returns a class containing two template parameters, based on one of the two templated parameters (Variance).
My hierarchy can be simplified to this.
template<typename T>
class AbstractType {};
template<typename T, Variance Type>
class BaseType : public AbstractType<T> {};
template<typename T, Variance Type>
class FinalType : public BaseType<T, Type>{};
Where FinalType in this example isn't strictly necessary, it also inherits from other templated classes in my actual code, which serves no purpose in this example, so it's been removed.
Here's the example code.
enum class Variance {
Interval,
Weighted,
Constant
};
template<typename T>
class AbstractType
{
protected:
AbstractType(T* _Instance) :
Instance(_Instance) {};
T* Instance;
};
template<typename T, Variance Type>
class BaseType : public AbstractType<T>
{
protected:
BaseType(T* _Instance) :
AbstractType<T>(_Instance) {};
};
template<typename T>
class BaseType<T, Variance::Interval> : public AbstractType<T>
{
protected:
BaseType(T* _Instance) :
AbstractType<T>(_Instance) {};
int Interval;
public:
void SetInterval(int NewInterval) { Interval = NewInterval; }
};
template<typename T>
class BaseType<T, Variance::Weighted> : public AbstractType<T>
{
protected:
BaseType(T* _Instance) :
AbstractType<T>(_Instance) {};
int Weight;
public:
void SetWeight(int NewWeight) { Weight = NewWeight; }
};
template<typename T, Variance Type>
class FinalType : public BaseType<T, Type>
{
public:
FinalType(T* _Instance) :
BaseType<T, Type>(_Instance)
{};
};
struct Interface
{
template<typename T>
BaseType<T, Variance::Weighted>* CreateBaseInstance(T t, int Weight) {
FinalType<T, Variance::Weighted>* OutObj = new FinalType<T, Variance::Weighted>(t);
OutObj.SetWeight(Weight);
return OutObj;
}
template<typename T>
BaseType <T, Variance::Interval>* CreateBaseInstance(T t, int Interval) {
FinalType<T, Variance::Weighted>* OutObj = new FinalType<T, Variance::Interval>(t);
OutObj.SetInterval(20);
return OutObj;
}
};
struct Object {};
void test()
{
Interface Inter;
Object Obj;
Inter.CreateBaseInstance<Object, Variance::Weighted>(Obj, 50); // Too many template arguments
Inter.CreateBaseInstance<Object, Variance::Interval>(Obj, 10); // Too many template arguments
Inter.CreateBaseInstance<Object>(Obj, 50); // More than one instance of overloaded function
Inter.CreateBaseInstance<Object>(Obj, 10); // More than one instance of overloaded function
}
Additionally, Object being used as the first template is just for testing purposes. I will not know the type in this codebase.
I've been able to resolve this problem by creating multiple functions such as..
template<typename T>
BaseType<T, Variance::Interval>* CreateIntervalInstance(T t, int Interval)…
template<typename T>
BaseType<T, Variance::Weighted>* CreateWeightedInstance(T t, int Weight)…
template<typename T>
BaseType<T, Variance::Constant>* CreateConstantInstance(T t)…
However, as mentioned earlier, FinalType inherits from another set of classes, so while the above works, it becomes crowded and unsustainable with the number of functions needed to do the simple tasks above.
I have experimented with making the Interface have a template parameter, such as..
// Base Class
template<Variance Variant>
class Interface…
// Specialized
template<>
class Interface<Variance::Interval>…
// Functions to create Interval Instances
//Specialized
template<>
class Interface<Variance::Weighted>…
// Functions to create Weighted Instances
But, I once again run into it becoming unsustainable.
The last thing I am currently looking into is Type Traits, but am unsure if or how that could be implemented to make this simpler.
Without following all of it really in detail, don't you just want e.g.
template<typename T, Variance Type>
auto CreateBaseInstance(T t, int value) {
// Instead of `auto` maybe `std::unique_ptr<BaseType<T, Type>>`
auto OutObj = std::make_unique<FinalType<T, Type>>(t);
if constexpr(Type == Variance::Weighted) {
OutObj->SetWeight(value);
} else if constexpr(Type == Variance::Interval) {
OutObj->SetInterval(value);
}
return OutObj;
}
(I replaced new with std::make_unique, because using raw new like this is generally considered bad practice due to the memory management problems it causes.)

pointer-to-member type and template

First I got to class for this test:
struct Foo {
void test() {
std::cout << "test" << std::endl;
}
};
struct Bar {
Foo foo;
};
and a template class which accepts a pointer-to-member when constructing:
template<typename Type, typename MemberType>
class test {
public:
test(Type &t, MemberType Type::* p) : t(t) {
(t.*p).test();
}
Type &t;
};
it works well, instantiation can be done by using test<Bar, Foo> test(bar, &Bar::foo);, except that it is boring to write Bar and Foo every time since it can be deduced for &Bar::foo, so I decide to make a specialization version:
template<typename Type, typename MemberType, MemberType Type::*p>
class test<MemberType Type::*p> {
public:
test(Type &t) {
(t.*p).test();
}
};
ok, I don't really know how to do this, I just want to pass a MemberType Type::*p as the only template parameter like this: test<&Bar::foo> test(bar);
Then I create a brand new class:
template<typename Type, typename MemberType, MemberType Type::*p>
class test2 {
public:
test2(Type &t) {
(t.*p).test();
}
};
now I can pass as a template argument rather than a constructor argument, but this time I have to write three paramters:test2<Bar, Foo, &Bar::foo> test2(bar);
So what's the right way on earth that I can simply use test<&Bar::foo> test(bar);?
You almost got it correctly.
The primary template has to use an auto template parameter (a C++17 feature):
template <auto MemberPtr> class test {};
Then the specialization:
template <typename Type, typename MemberType, MemberType Type::*p>
class test<p> // Note `p` instead of `MemberType Type::*p`.
{
public:
test(Type &t)
{
(t.*p).test();
}
};
An alternative: test t(bar, &Bar::foo);
This use your primary template:
template<typename Type, typename MemberType>
class test {
public:
test(Type &t, MemberType Type::* p) : t(t) {
(t.*p).test();
}
Type &t;
};
and template argument deduction (TAD).
Live demo: https://coliru.stacked-crooked.com/a/52ee3ddba011473b
From C++17 onwards this is the exact intended use case for user defined deduction guides. Pre-C++17 one often used make functions for this.
#include <iostream>
template<typename Type, typename MemberType>
class test {
public:
test(Type &t, MemberType Type::* p) : t(t) {
(t.*p).test();
}
Type &t;
};
// C++17 user defined deduction guide
template<typename Type, typename MemberType>
test(Type &t, MemberType Type::* p) -> test<std::decay_t<decltype(t)>, decltype(static_cast<std::decay<decltype(t)>>(t).*p) >;
// pre C++17 make function
template<typename Type, typename MemberType>
test<Type, MemberType> make_test(Type &t, MemberType Type::* p) {
return test<Type, MemberType>(t, p);
}
struct Foo {
void test() {
std::cout << "test" << std::endl;
}
};
struct Bar {
Foo foo;
};
int main() {
Bar b;
test<Bar, Foo> t1 (b, &Bar::foo);
// Only works in C++17
test t2(b, &Bar::foo);
auto t3 = make_test(b, &Bar::foo);
}

Conditional compilation and template

Suppose, I have a code:
template <typename T>
class C {
public:
T f() { return m_result; }
void todo() { m_result = doit<T>(); }
private:
T m_result;
};
If T is void, I want to return void and have no m_result at all.
But, the compiler does not allow instantiate a void type.
One of decision is to create a specialization.
template <> class C<void> { /* ... */ }
But I don't what to support the almost identical code.
How can I don't instantiate m_result?
I can use C++17. Thanks!
You could place the data in a base class, then use if constexpr:
template<class T>
struct C_data{
T m_result;
};
template<>
struct C_data<void>{
};
template<class T>
class C: C_data<T>
{
static constexpr auto is_void = std::is_same_v<T,void>;
public:
auto f(){
if constexpr(is_void)
return this->m_result;
else
return;
}
void todo(){
if constexpr(is_void)
this->m_result = doit<T>();
else
doit<T>();
}
};
But it can be argued that a the specialization of the class C is cleaner since all member of a template class should depend on all the template parameter (otherwise you should split your class in order to avoid code bloat).
So I would prefer to fully specialize C, and make part of the class C that are independent of T, a base class of C:
class C_base{
//any thing that is independent of T;
};
template<class T>
class C: public C_base{
//any thing that depend on T
};
template<>
class C<void>: public C_base{
//any thing that depend on T;
};
You could also specialize member funtion by member function, but I find it less clean.
You will find this last code structure in almost all headers of standard library implementations.
This works for me:
#include <type_traits>
template <typename T> T doit() { return T{}; }
template <typename T> struct result_policy { T m_result; };
template <> struct result_policy<void> { };
template <typename T>
class C : private result_policy<T> {
public:
T f(){
if constexpr (!std::is_void_v<T>)
return result_policy<T>::m_result;
}
void todo() {
if constexpr(!std::is_void_v<T>)
result_policy<T>::m_result = doit<T>();
}
};
int main() {
C<int> ci;
ci.todo();
int i = ci.f();
C<void> cv;
cv.todo();
cv.f();
}
I used if constexpr from C++17 to work with m_result and stored m_result into policy struct only for non-void types due to partial template specialization.
If you can use C++17, then try with if constexpr, std::is_same_v<> and std::conditional_t<>:
#include <type_traits>
// auxiliary variable template for checking against void type in a more readable way
template<typename T>
constexpr bool is_void_v = std::is_same_v<T, void>;
// auxiliary alias template for determining the type of the data member
// in order to prevent the compiler from creating member of type "void"
// so if T = void --> data member type as "int"
template<typename T>
using member_type_t = std::conditional_t<!is_void_v<T>, T, int>;
template <typename T>
class C{
public:
T f(){ return (T)m_result; } // no problem if T = void
void todo() {
if constexpr(!is_void_v<T>)
m_result = doit<T>();
else
doit<T>();
}
private:
member_type_t<T> m_result;
};
Actually, as of C++17 there is already a std::is_void_v<> variable template with type_traits.

Conditional member signature and implementation based on template type parameter

I am trying to write a template class with multiple type parameters T1 and T2. The class has a private member of type std::promise<T2>.
template <class T, class T2>
class Test
{
public:
void setValue(T2 value)
{
promise.set_value(value);
}
void setValue()
{
promise.set_value();
}
private:
std::promise<T2> promise;
};
This class compiles just fine when T2 is anything but void (as long as you don't call setValue without parameters. When T2 is void, I get a compiler error:
error C2182: 'value' : illegal use of type 'void'
When T2 is anything but void, I would like to use the first setValue method, which has a single parameter of type T2. When T2 is void, I would like to use the second setValue method, which takes no parameters. I've looked at a lot of examples, but I am relatively new to template programming, and I can't seem to make it work.
Is it possible to accomplish this with std::enable_if somehow? Or with template specialization?
helper template class specialisation:
#include <future>
template<typename T>
class TestHelper
{
public:
void setValue(T const& v)
{ promise.set_value(v); }
private:
std::promise<T> promise;
};
template<>
class TestHelper<void>
{
public:
void setValue()
{ promise.set_value(); }
private:
std::promise<void> promise;
};
template <class T, class T2>
class Test : public TestHelper<T2>
{
};
int main()
{
Test<void, int> t;
// t.setValue(); // compilation error: no matching function for call to 'std::promise<int>::set_value()'
t.setValue(0);
Test<void, void> t1;
t1.setValue();
// t1.setValue(0); // compilation error: no matching function for call to 'std::promise<void>::set_value(int)'
}
You can solve this problem with a conditional dependency on a base class:
#include <future>
#include <type_traits>
#include <iostream>
template<class T2>
struct Base {
protected:
std::promise<T2> promise;
};
template<class T2>
struct BaseWithVariable : public Base<T2> {
void setValue(T2 value)
{
this->promise.set_value(value);
}
};
template<typename T2>
struct BaseWithoutVariable : public Base<T2> {
void setValue()
{
this->promise.set_value();
}
};
template<typename T, typename T2>
class Test
: public std::conditional<std::is_same_v<T2, void>, BaseWithoutVariable<T2>, BaseWithVariable<T2>>::type
{
};
int main()
{
Test<int, int> a;
a.setValue(5);
Test<int, void> b;
b.setValue();
}
Now you realize that you can achieve the same with specialization on the level of the intermediate class:
template<class T2>
struct BaseSetter : public Base<T2> {
void setValue(T2 value)
{
this->promise.set_value(value);
}
};
template<>
struct BaseSetter<void> : public Base<void> {
void setValue()
{
this->promise.set_value();
}
};
template<typename T, typename T2>
class Test : public BaseSetter<T2>
{
};
And it would also not hurt, in this particular case, to omit the use of Base and just have both variants of BaseSetter use their own member variable std::promise<T2>, or std::promise<void>, respectively.
However all of these crash in runtime with GCC 7.2.0. I don't know why.

Mechanism to create "template" objects in C++

I want to be able to initialize objects with some default values, but to do this from external code(not embedded in the class itself). The objects are exposed to external editor and I don't want to set the same values again and again and change only some values that are different. As I have already template classes I want to do this from the "traits" class.
This is a simple samle of what I want to achieve:
template<typename Traits>
class Test
{
public:
Test()
{
//if Traits has Init init function call Traits::Init(this)
}
private:
typename Traits::Type value;
friend Traits;
};
struct TestTraits
{
typedef std::string Type;
};
struct TestTraitsInit
{
typedef int Type;
static void Init(Test<TestTraitsInit>* obj)
{
obj->value = 0;
}
};
int main()
{
Test<TestTraits> obj1;
Test<TestTraitsInit> obj2;
}
As you can see it makes sense to have Init() only in some cases. Is it possible to check if class Traits has Init() function and call it only when it exists?
I know that a very simple solution would be to have empty Init() functions, but I want a better solution:)
You could create some class template maybe_call_init with a proper SFINAE-constrained specialization based on expression SFINAE:
template<typename T, typename = void>
struct maybe_call_init
{
static void maybe_call(Test<T>* obj) { }
};
template<typename T>
struct maybe_call_init<T,
decltype(T::Init(std::declval<Test<T>*>()), void(0))>
{
static void maybe_call(Test<T>* obj) { T::Init(obj); }
};
Given a trait T, maybe_call_init<T>::maybe_call(obj) will call T::Init(obj) if T defines such a function, and it will do nothing otherwise.
Then, you could use it in your original class template this way:
template<typename Traits>
class Test
{
public:
Test()
{
maybe_call_init<Traits>::maybe_call(this);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
private:
typename Traits::Type value;
friend Traits;
};
The above solution is a bit rudimental, and could be improved by hiding the maybe_call_init class template and its specialization in a detail namespace, providing a helper function to do the instantiation work. So given this machinery:
namespace detail
{
template<typename T, typename U, typename = void>
struct maybe_call_init
{
static void maybe_call(U* obj) { }
};
template<typename T, typename U>
struct maybe_call_init<T, U,
decltype(T::Init(std::declval<U*>()), void(0))>
{
static void maybe_call(U* obj) { T::Init(obj); }
};
}
template<template<typename> class T, typename U>
void maybe_call_init(T<U>* obj)
{
detail::maybe_call_init<U, T<U>>::maybe_call(obj);
}
The constructor of your original Test class may now look like this:
template<typename Traits>
class Test
{
public:
Test()
{
maybe_call_init(this);
// ^^^^^^^^^^^^^^^^^^^^^
}
public:
typename Traits::Type value;
friend Traits;
};
Here is a live example.