Calling a templated method allowing only subclasses as parameter - c++

Suppose I have set of classes inheriting from a single superclass S:
class S{ ... };
class C1 : public S{ ... };
class C2 : public S{ ... };
Then suppose I have a templated method:
template<class T> void foo(T* instance);
I would like to statically check that foo is never called providing an instance of the superclass but only called providing one of the (concrete) subclasses (e.g. explicitly calling foo<C1>(x) for instance)
Is this possible?

First we can write a trait to check if T is derived from S, but not S:
template <class Base, class Derived>
using is_strict_base =
std::integral_constant<bool,
std::is_base_of<Base,Derived>::value &&
!std::is_same<Base,typename std::remove_cv<Derived>::type>::value>;
You can use std::enable_if to use this trait:
template<class T>
typename std::enable_if<is_strict_base<S,T>::value>::type
foo(T* instance)
{}
With C++14 you can use std::enable_if_t to make it a bit prettier:
template<class T>
std::enable_if_t<is_strict_base<S,T>::value>
foo(T* instance)
{}
Another option is to use static_assert:
template<class T>
void foo(T* instance)
{
static_assert(is_strict_base<S,T>::value,
"T must be derived from S, but not S");
}
This gives you a nicer error, but I personally believe that type constraints for a function belong in the declaration.

Related

Conditionally apply mixin to interface, depending on presence of virtual method

I've got a bunch of similar interfaces. I have a class template that can implement those interfaces and implement most operations that are common to those interfaces. As a result, I use a mixin on top of those interfaces.
Unfortunately, some of those interfaces are missing some of the common methods.
struct Intf1
{
virtual void f() = 0;
};
struct Intf2 {}; // Missing f.
I want to apply a mixin
template <class T>
struct Mixin
: public T
{
virtual void f() override {};
};
to a base, exactly if the base class declares a virtual method f that the mixin overrides. So, basically I am looking for some meta-programming technique
template <template <class> class Mixin, class T> using Magic = ...;
such that Magic<Mixin, Intf1> is Mixin<Intf1> and Magic<Mixin, Intf2> is Intf2.
At the moment, I am using a separate trait to check whether a method is available in the base class, but this solution has some problems:
I have to reiterate the function signature.
I do not definitely know whether the method is virtual (though it seems, this can be done).
Due to type conversion / const-ness, I may try to override a method that does not match my method's signature perfectly.
I was hoping that there might be a better solution. If my Mixin was a class that caused a substitution failure, I'd try something like this: Godbolt.
it's impossible to detect if a function is a virtual function.
but if all pure virtual functions of T is overridden by Mixin, it works:
#include <type_traits>
struct Intf1{
virtual void f() = 0;
};
struct Intf2 {};
struct Intf3{
virtual void f2() = 0;
};
template <class T>
struct Mixin : public T{
virtual void f() {}; // don't mark 'override', it will break SFINAE.
};
template<template<typename>class MixIn, typename T, typename = void>
struct Applier{
typedef T type;
};
template<template<typename>class MixIn, typename T>
struct Applier<MixIn, T, typename std::enable_if<std::is_abstract<T>::value && std::is_abstract<MixIn<T>>::value>::type>{
typedef T type;
};
template<template<typename>class MixIn, typename T>
struct Applier<MixIn, T, typename std::enable_if<std::is_abstract<T>::value && !std::is_abstract<MixIn<T>>::value>::type>{
typedef MixIn<T> type;
};
template<template<typename>class MixIn, typename T>
using applier_t = typename Applier<MixIn, T>::type;
static_assert(std::is_same<applier_t<Mixin, Intf1>, Mixin<Intf1>>::value, "overridden");
static_assert(std::is_same<applier_t<Mixin, Intf2>, Intf2>::value, "not abstract");
static_assert(std::is_same<applier_t<Mixin, Intf3>, Intf3>::value, "not overridden");

C++ functor (mapping)

I have created a class either<l, r> much like Haskell's Either a b. I have also implemented a function map directly in the class; this is what the code looks like:
template<typename l, typename r>
class either
{
template<typename b>
either<l, b> map(const std::function<b (r)> &f)
{
// ...
}
};
Now I want to isolate the map function in order to create an abstract base class called functor
template<typename a, template <a> class derived>
class functor
{
public:
virtual ~functor();
template<typename b>
derived<b> map(const std::function<b (a)> &f) = nullptr;
};
either would inherit this class:
class either : functor<r, either<l, r>>
however this is invalid C++ as template member functions can not be virtual.
Moreover, I have attempted to test if <r, either<l, r>> can match <a, derived<a>> in functor<r, either<l, r>> (or any other template for that matter) but haven't been able to since it has two template parameters. Also note that other derived classes of functor might have different numbers of template arguments that are irrelevant to functor.
Can the functor base class be expressed in C++ templates?
Since you are using the curiously recurring template pattern, there's no need for virtual functions. Your goal of dispatching to the method of the derived class can be achieved directly from the base class.
A typical implementation would be the following :
#include <iostream>
#include <functional>
using namespace std;
template<typename a, class derived>
class functor
{
public:
// You have to define the destructor
virtual ~functor() {}
template<typename b>
// The full type of the derived class is known
derived map(const std::function<b(a)> &f)
{
// Here you take advantage of the CRTP
return static_cast<derived*>(this)->map(f);
}
};
template<typename l, typename r>
// You have to put public as inheritance access level
class either : public functor<r, either<l, r>>
{ // The way you want to inherit implies that the base has no
// template template parameter, just template parameters !!
public:
template<typename b>
either<l, b> map(const std::function<b(r)> &f)
{
cout << "In derived" << endl;
return either<l, b>();
}
};
int main()
{
// pointer to base class points to a derived object
functor<int, either<int, int>> *ff = new either<int, int>();
// map function will call the method of the derived class
ff->map<int>([](int k){ return 1; });
return 0;
}
I took the liberty of pointing some things out in the comments. HTH

Template and inheritance

If I have a base class and its hierarchy :
class BaseClass {
}
class DerivedClass : public BaseClass {
}
And a templated class :
template <class T> struct TemplatedClass {
}
How can I achieve this ? :
// 1:
void doSomething(TemplatedClass<BaseClass *> const &t);
// 2:
std::vector<TemplatedClass<BaseClass *>> v;
TemplatedClass<DerivedClass *> a;
// Doesn't compile
doSomething(a);
// Doesn't compile
v.push_back(a);
You could have your templated classes have a hierarchy, too. You'll need to specify the bases, however:
template <typename...>
struct TC;
template <>
struct TC<> {
virtual ~TC() {}
};
template <typename T, typename... B>
struct TC
: TC<B...> {
// ...
};
With the variadic argument for the inheritance this should allow you specifying the relationship between the templates to mimick the inheritance hierarchy of the underlying. For example:
TC<Base>* d = new TC<Derived, Base>(/*...*/);
You need to make TemplatedClass<DerivedClass*> convertable to TemplatedClass<BaseClass *>, as they are independent types. Best way would be to have constructor, something like this:
template <class T> struct TemplatedClass {
template<class P> TemplatedClass( const TemplatedClass<P> &an );
};
Looks like you are trying to make something like smart pointer, you should look how it is already done on std::shared_ptr or boost::shared_ptr.
If you plan to use this code in production and/or share it with your team you may want to put additional checks on data type passed to this ctor:
template <class T> struct TemplatedClass {
template<class P, class = typename std::enable_if<std::is_convertible<P, T>{}>::type>
TemplatedClass( const TemplatedClass<P> &an );
};
Again best way would be to look into sources of shared_ptr and see how it is done there.

Templated deriving from a template

Given two templated classes which are very similar but behave differently:
template<class T>
firstBase {};
template<class T>
secondBase {};
Now I've got this other class, which, based on it's template parameter, will derive either from firstBase or from secondBase:
template<class B, class T>
myClass : public B<T> { /* T is used in here */ };
Well, that does not work. The compiler tells me that B is an unknown template name. (error: unknown template name 'B')
My current workaround is to define myClass as
template<class B, class T>
myClass : public B { /* T is used in here */ };
and the caller of myClass needs to intatiate it via myClass<b<t>, t> instead of myClass<b, t>.
The latter would be really nice and reduce some copy&paste code. Is there any other way of achieving this?
In my use case I'm trying to implement a deep_const_ptr for the pimpl idiom enabling 'true constness'. Depending on whether myClass needs to be copy-assignable or not, it either uses deep_const_ptr<std::shared_ptr> or deep_const_ptr<std::unique_ptr> for its private pointer.
#include <memory>
#include <iostream>
template<class pointerT, class typeT>
class deep_const_ptr : public pointerT
{
public:
explicit deep_const_ptr(typeT* ptr) : pointerT(ptr) { }
// overloading pointerT::operator->() for non-constant access
typeT* operator->() {
std::cout << "deep_const_ptr::operator->()" << std::endl;
return pointerT::operator->();
}
// overloading pointerT::operator->() for constant access
const typeT* operator->() const {
std::cout << "deep_const_ptr::operator->() const" << std::endl;
return pointerT::operator->();
}
};
Edit
So I ended up, as suggested by Luc Danton in his answer, passing std::unique_ptr<myClass::Private> or std::shared_ptr<myClass::Private> to my custom deep_const_ptr:
template<typename pointerTypeT>
class deep_const_ptr : public pointerTypeT {
explicit deep_const_ptr(typename pointerTypeT::element_type* ptr) : pointerTypeT(ptr);
typename pointerTypeT::element_type* operator->();
const typename pointerTypeT::element_type* operator->() const;
};
deep_const_ptr<std::unique_ptr<Test::Private>> d_unique;
deep_const_ptr<std::shared_ptr<Test::Private>> d_shared;
What you want is template template parameter:
// vvvvvvvvvvvvvvvvvv
template<template<typename> class B, class T>
class myClass : public B<T> { /* T is used in here */ };
Then, use that like:
myClass<firstBase,int> myIntClassObject;
myClass<secondBase,bool> myBoolClassObject;
For std::unique_ptr, you can make a wrapper:
template<typename T>
class uniquePtr : public std::unique_ptr<T>
{
};
or
template<typename T>
using uniquePtr = std::unique_ptr<T>;
I recommend against template template parameters in general. For instance std::unique_ptr and std::shared_ptr are different in that the former accepts two type parameters and the latter just one. So if you declare e.g. template<template<typename> class B, typename T> class foo; then foo<std::shared_ptr, int> is valid but foo<std::unique_ptr, int> isn't.
In your particular case you could use template<typename...> class B as a parameter, because that kind of template template parameters is special. Still, this would only accept templates that only take type parameters (not even template template parameters). Sometimes this can be worked around with alias templates, sometimes it can't.
In my experience there are better alternatives -- for instance you can conditionally inherit:
template<typename T>
struct pointer: std::conditional</* Make a choice here*/, std::unique_ptr<T>, std::shared_ptr<T>>::type {};
Or why not just accept the smart pointer as the parameter itself:
template<typename Pointer>
struct foo {
Pointer pointer;
/* typename Pointer::element_type plays the role that T used to have */
};
The Standard Library itself takes some steps to avoid template template parameters: have you ever noticed that an std::vector<T> uses std::allocator<T> as an argument, and not std::allocator? As a tradeoff, it means that an Allocator must provide a rebind member alias template.
B should be a template template parameter:
template<template <class> class B, class T>
class myClass : public B<T> { /* T is used in here */ };
Now you can give either firstBase or secondBase as the first template argument because they are templates.
You simply need to use a template-template parameter to get it to work:
template <
template <class> class B, class T>
// ^^^^^^^^^^^^^^^^
class myClass : public B<T> { / ... / };
It seems what you need is template template parameters:
template<template<class> class B, class T>
// ^^^^^^^^^^^^^^^
class myClass : public B<T> { /* T is used in here */ };
Now the first template argument for the myClass class template must be itself a class template that accepts one template (type) parameter. So, putting everything together:
template<class T>
class firstBase {};
template<class T>
class secondBase {};
template<template<class> class B, class T>
class myClass : public B<T> { /* T is used in here */ };
And here is how you would instantiate your myclass template to create a class that derives from firstBase<int>:
myClass<firstBase, int> obj;
Finally, here is a live example.

Can you template specialize a subclass that is not templated?

Here is my situation:
Base class, no templated type:
struct Thing
{
} ;
Templated class, extends that very base class
template <typename T> struct VertexWriter : public Thing
{
template <typename S>
bool intersects( S* otherThing )
{
// has a body, returns T or F
}
} ;
Derived class, CONCRETE type (no template)
struct Rocket : VertexWriter<VertexPNCT>
{
template <typename S>
bool intersects( S* otherThing ) ; // WANTS TO OVERRIDE
// implementation in VertexWriter<T>
} ;
But template typename<S> bool VertexWriter<T>::intersects cannot be marked virtual, because it is a template class.
There are many classes that derive from the VertexWriter<VertexPNCT> specialization, so template specializing VertexWriter<VertexPNCT> would not work.
So the normal thing to do is to provide a template specialization.
But Rocket specifies it is a VertexWriter<VertexPNCT>, so it is no longer a template class. Can it specialize OR override intersects as if it were a virtual function?
No, as you've stated you cannot use virtual functions, which would provide runtime polymorphism.
That said, depending on what leeway you have on changing the layout of your classes, you might be able to do something using CRTP.
template <typename T, typename Derived> struct VertexWriter : public Thing
{
template <typename S>
bool intersects( S* otherThing )
{
return static_cast<Derived*>(this)->insersects_impl(otherThing);
}
template<typename S>
bool insersects_impl( S* otherThing)
{
// Whatever
}
} ;
struct Rocket : VertexWriter<VertexPNCT, Rocket>
{
template <typename S>
bool intersects_impl( S* otherThing ) { ... } // WANTS TO OVERRIDE
// implementation in VertexWriter<T>
} ;
The base implementation of intersects simply forwards to the CRTP function. If the derived class overrides it, it'll use the override, but otherwise it'll fall back the default. Note, this does complicate your class hierarchy, but might accomplish what you're looking for.
No, you cannot.
Templated member functions cannot be virtual.
It doesn't matter whether VertexWriter is templated, specialized, or a normal class.
You could template VertexWriter on S and make intersects a virtual function of that class template
template <typename T, typename S> struct VertexWriter : public Thing
{
virtual bool intersects( S* otherThing )
{
// has a body, returns T or F
}
} ;
Now you can override intersects in a derived class
template<typename S>
struct Rocket : VertexWriter<VertexPNCT, S>
{
virtual bool intersects( S* otherThing ) ; // WANTS TO OVERRIDE
// implementation in VertexWriter<T>
}