In a templated class, can we check a member function's signature and define different compilation behaviors for different subclasses? To be more specific, consider the following simple example:
template <typename T>
class Foo {
// ingore all other functions
virtual std::shared_ptr<T> do_something(int a) {
return std::make_shared<T>(a);
}
};
This should just work fine with the T1 class:
class T1 {
T1(int a) {
// implememntation
}
// ingore all other functions
};
// Foo<T1> would just work
However, it will fail the compilation with T2 because Foo's do_something function is clearly implemented to call T's constructor with one argument only:
class T2 {
T2(int a, int b) {
// implememntation
}
// ingore all other functions
};
// Foo<T2> would fail to compile
So the question is, can we rework on Foo to let it work for both T1 and T2, in the way that, for T1 and classes whose constructor takes one int argument, it will compile with a default implementation, whereas for T2 and classes whose constructors are different, it will compile with a virtual function and enforce subclass to implement it with override. Somewhat like below:
Template <typename T>
class Foo {
// ingore all other functions
/* if T's signature takes one int input only
* compile like the below
*/
virtual std::shared_ptr<T> do_something(int x) {
return std::make_shared<T>(x);
}
/* if T's signature is different
* compile like the below and let the subclass implement it
*/
virtual std::shared_ptr<T> do_something(int x) = 0;
}
Is this possible without using any 3rd-party library? It's acceptable if we have to use macros/preprocessors.
It is also acceptable if there must be one function do_something there but for the case of mismatched signature, it just raises runtime exception, something like:
Template <typename T>
class Foo {
// ingore all other functions
virtual std::shared_ptr<T> do_something(int x) {
/* if T's signature takes one int input only
* compile like the below
*/
// return std::make_shared<T>(x);
/* if T's signature is different
* compile like the below and let the subclass implement it
*/
// std::throws...
}
}
As far as I can tell, we need class template specialization here. Not even C++20 requires-clauses can be applied to virtual functions, so the only thing we can do is have the whole class change.
template<typename T> // using C++20 right now to avoid SFINAE madness
struct Foo {
virtual ~Foo() = default;
virtual std::shared_ptr<T> do_something(int a) = 0;
};
template<std::constructible_from<int> T>
struct Foo<T> {
virtual ~Foo() = default; // to demonstrate the issue of having to duplicate the rest of the class
virtual std::shared_ptr<T> do_something(int a) {
return std::make_shared<T>(a);
}
};
If there is a lot of stuff in Foo, you can avoid duplicating it with a heap of upfront cost by moving do_something to its own class.
namespace detail { // this class should not be touched by users
template<typename T>
struct FooDoSomething {
virtual ~FooDoSomething() = default;
virtual std::shared_ptr<T> do_something(int a) = 0;
};
template<std::constructible_from<int> T>
struct FooDoSomething<T> {
virtual ~FooDoSomething() = default;
virtual std::shared_ptr<T> do_something(int a);
};
}
template<typename T>
struct Foo : detail::FooDoSomething<T> {
// other stuff, not duplicated
// just an example
virtual int foo(int a) = 0;
};
namespace detail {
template<std::constructible_from<int> T>
std::shared_ptr<T> FooDoSomething<T>::do_something(int a) {
Foo<T> &thiz = *static_cast<Foo<T>*>(this); // if you need "Foo things" in this default implementation, then FooDoSomething is *definitely* unsafe to expose to users!
return std::make_shared<T>(thiz.foo(a));
}
}
Godbolt
To convert this down from C++20, replace the concept-based specialization with old-type branching:
// e.g. for the simple solution
template<typename T, bool = std::is_constructible_v<T, int>>
struct Foo { // false case
// etc.
};
template<typename T>
struct Foo<T, true> { // true case
// etc.
};
// or do this to FooDoSomething if you choose to use that
A runtime error is much easier, at least in C++17 and up, since you can just use a if constexpr to avoid compiling the problematic code
template<typename T>
struct Foo {
virtual ~Foo() = default;
virtual std::shared_ptr<T> do_something(int a) {
if constexpr(std::is_constructible_v<T, int>) return std::make_shared<T>(a);
else throw std::logic_error("unimplemented Foo<T>::do_something");
}
};
Related
I have some code that basically do this:
struct Base {
virtual ~Base() = default;
virtual int forward() = 0;
};
struct Derived : Base {
int forward() override {
return 42;
}
};
typename std::aligned_storage<sizeof(Derived), alignof(Derived)>::type storage;
new (&storage) Derived{};
auto&& base = *reinterpret_cast<Base*>(&storage);
std::cout << base.forward() << std::endl;
I highly doubt it's well defined behaviour. If it's indeed undefined behaviour, how can I fix it? In the code that do the reinterpret_cast, I only know the type of the base class.
On the other hand, if it's well defined behavior in all cases, why is this working and how?
Just keeping a reference to the contained object is not applicable here. In my code I want to apply SBO on a type erased list where the type is created by the user of my library, and basically extends the Base class.
I add elements inside a template function, but in the function that reads it, I cannot know the Derived type. The whole reason why I use a base class is because I only need the forward function in my code that reads it.
Here's what my code looks like:
union Storage {
// not used in this example, but it is in my code
void* pointer;
template<typename T>
Storage(T t) noexcept : storage{} {
new (&storage) T{std::move(t)}
}
// This will be the only active member for this example
std::aligned_storage<16, 8> storage = {};
};
template<typename Data>
struct Base {
virtual Data forward();
};
template<typename Data, typename T>
struct Derived : Base<Data> {
Derived(T inst) noexcept : instance{std::move(inst)} {}
Data forward() override {
return instance.forward();
}
T instance;
};
template<typename> type_id(){}
using type_id_t = void(*)();
std::unordered_map<type_id_t, Storage> superList;
template<typename T>
void addToList(T type) {
using Data = decltype(type.forward());
superList.emplace(type_id<Data>, Derived<Data, T>{std::move(type)});
}
template<typename Data>
auto getForwardResult() -> Data {
auto it = superList.find(type_id<Data>);
if (it != superList.end()) {
// I expect the cast to be valid... how to do it?
return reinterpret_cast<Base<Data>*>(it->second.storage)->forward();
}
return {};
}
// These two function are in very distant parts of code.
void insert() {
struct A { int forward() { return 1; } };
struct B { float forward() { return 1.f; } };
struct C { const char* forward() { return "hello"; } };
addToList(A{});
addToList(B{});
addToList(C{});
}
void print() {
std::cout << getForwardResult<int>() << std::endl;
std::cout << getForwardResult<float>() << std::endl;
std::cout << getForwardResult<const char*>() << std::endl;
}
int main() {
insert();
print();
}
Not sure about the exact semantics of whether reinterpret_cast is required to work with base class types, but you can always do this,
typename std::aligned_storage<sizeof(Derived), alignof(Derived)>::type storage;
auto derived_ptr = new (&storage) Derived{};
auto base_ptr = static_cast<Base*>(derived_ptr);
std::cout << base_ptr->forward() << std::endl;
Also why use the auto&& with the base reference in your code?
If you only know the type of the base class in your code then consider using a simple trait in an abstraction for the aligned_storage
template <typename Type>
struct TypeAwareAlignedStorage {
using value_type = Type;
using type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
};
and then you can now use the storage object to get the type it represents
template <typename StorageType>
void cast_to_base(StorageType& storage) {
using DerivedType = std::decay_t<StorageType>::value_type;
auto& derived_ref = *(reinterpret_cast<DerivedType*>(&storage));
Base& base_ref = derived_ref;
base_ref.forward();
}
If you want this to work with perfect forwarding, then use a simple forwarding trait
namespace detail {
template <typename TypeToMatch, typename Type>
struct MatchReferenceImpl;
template <typename TypeToMatch, typename Type>
struct MatchReferenceImpl<TypeToMatch&, Type> {
using type = Type&;
};
template <typename TypeToMatch, typename Type>
struct MatchReferenceImpl<const TypeToMatch&, Type> {
using type = const Type&;
};
template <typename TypeToMatch, typename Type>
struct MatchReferenceImpl<TypeToMatch&&, Type> {
using type = Type&&;
};
template <typename TypeToMatch, typename Type>
struct MatchReferenceImpl<const TypeToMatch&&, Type> {
using type = const Type&&;
};
}
template <typename TypeToMatch, typename Type>
struct MatchReference {
using type = typename detail::MatchReferenceImpl<TypeToMatch, Type>::type;
};
template <typename StorageType>
void cast_to_base(StorageType&& storage) {
using DerivedType = std::decay_t<StorageType>::value_type;
auto& derived_ref = *(reinterpret_cast<DerivedType*>(&storage));
typename MatchReference<StorageType&&, Base>::type base_ref = derived_ref;
std::forward<decltype(base_ref)>(base_ref).forward();
}
If you are using type erasure to create your derived class types which you then add to a homogenous container, you could do something like this
struct Base {
public:
virtual ~Base() = default;
virtual int forward() = 0;
};
/**
* An abstract base mixin that forces definition of a type erasure utility
*/
template <typename Base>
struct GetBasePtr {
public:
Base* get_base_ptr() = 0;
};
template <DerivedType>
class DerivedWrapper : public GetBasePtr<Base> {
public:
// assert that the derived type is actually a derived type
static_assert(std::is_base_of<Base, std::decay_t<DerivedType>>::value, "");
// forward the instance to the internal storage
template <typename T>
DerivedWrapper(T&& storage_in) {
new (&this->storage) DerivedType{std::forward<T>(storage_in)};
}
Base* get_base_ptr() override {
return reinterpret_cast<DerivedType*>(&this->storage);
}
private:
std::aligned_storage_t<sizeof(DerivedType), alignof(DerivedType)> storage;
};
// the homogenous container, global for explanation purposes
std::unordered_map<IdType, std::unique_ptr<GetBasePtr<Base>>> homogenous_container;
template <typename DerivedType>
void add_to_homogenous_collection(IdType id, DerivedType&& object) {
using ToBeErased = DerivedWrapper<std::decay_t<DerivedType>>;
auto ptr = std::unique_ptr<GetBasePtr<Base>>{
std::make_unique<ToBeErased>(std::forward<DerivedType>(object))};
homogenous_container.insert(std::make_pair(id, std::move(ptr)));
}
// and then
homogenous_container[id]->get_base_ptr()->forward();
You may simply do
auto* derived = new (&storage) Derived{};
Base* base = derived;
So no reinterpret_cast.
In the 'simple' exmaple you have, since you are casting from derived to base, either static_cast or dynamic_cast will work.
The more complex use case will end in tears, because the underlying values of a base pointer and a derived pointer to the same object need not be equal. It might work today, but fail tomorrow:
reinterpret_cast does not play well with inheritance, especially multiple inheritance. If you ever to inherit from multiple basesand the first base class has size (or not have size if empty base optimization is not performed), reinterpret_cast to the second base class from an unrelated type will not apply the offset.
Overloading does not play well with overriding. Templated classes should not have virtual methods. Templated classes with virtual methods should not be used with too much type deduction.
The undefined behavior is fundamental to the manner in which MI is specified in C++, and unavoidable because you are trying to obtain something (in compile time) that you deliberated erased (in compile time). Just drop every virtual keyword from that class and implement everything with templates and everything will be more simple and correct.
Are you sure your derived class objects can fit within 16 bytes? You probably need some static_assert.
If you are willing to stomach the performance penalty introduced by virtual functions, why care about alignment?
Is it possible to make a certain template function have 2 accessibility level for some certain template parameter? (via splitting into 2 functions?)
class B{
enum ENU{
T0,T1,T2
}
template<ENU T=T0> someType f(){ ... } //want T1,T2 = public, T0 = private
};
Current usage (the solution should not change it):-
B b;
int aa=b.f<T0>(); //should fail
std::string bb=b.f<T1>();// should ok
Edit: B has a lot of functions like this.
Here is the full code (just in case someone want to edit or use) https://ideone.com/ryNCml.
I doubt what you are trying to do is possible, as specializing functions on a value ain't allowed in C++.
Though if you don't need enumerations, you could write something similar:
class B {
public:
struct T0{};
struct T1{};
struct T2{};
template<typename T> void f(T, ...) {
static_assert(std::is_same_v<T, T1> || std::is_same_v<T, T2>);
}
private:
void f(T0, ...) {}
};
int main(int argc, char **argv) {
B b{};
b.f(T1{}); // Should compile
b.f(T0{}); // Should not compile
}
If you would use the same function implementation, you can either forward this to a common method, or simply put T0 private.
Alternatively, you could make use of a proxy object which could convert the value, though I'm not sure if this is standard C++ of an extension of the compiler I'm familiar with:
class B {
public:
enum class T { //< Strong typed!
T0,
T1,
T2
}
template <T t>
struct TWrapper {};
template <T ActualT>
void f(..., TWrapper<ActualT> tw = TWrapper<ActualT>{});
private:
template <>
struct TWrapper<T0> {};
}
As far as I can understand, you want to forbid the use of T0 as a template parameter while invoking member method f.
To do that, you can use either std::enable_if or a static_assert.
It follows a minimal, working example:
#include<type_traits>
class B {
public:
enum ENU { T0,T1,T2 };
template<ENU T>
std::enable_if_t<(T==T1||T==T2),int>
f() { return 42; }
template<ENU T>
int g(){
static_assert(T==T1||T==T2, "not allowed");
return 42;
}
};
int main() {
B b;
b.f<B::T1>();
// It doesn't work
//b.f<B::T0>();
b.g<B::T1>();
// It doesn't work
//b.g<B::T0>();
}
Given you want to support only a finite set of template arguments, I would write three functions which are not template functions, and give them the right visibility. Then make them delegate to a private template function which does the work. This would look like:
class B{
public:
enum ENU{
T0,T1,T2
}
private:
template<ENU T=T0> int f(){
std::cout<<"In enum "<<T<<std::endl;
return 0;
}
protected:
someType fT0() { return f<T0>(); }
public:
someType fT1() { return f<T1>(); }
someType fT2() { return f<T2>(); }
};
Contrary to your requirements, the usage changes - but this is often the simplest approach:
B b;
int aa=b.fT0(); // fails
int bb=b.fT1();// ok
Alternatively, you can make the template be public, but give it a dummy argument (with a default), and make the type of the dummy argument depend on the template parameter (via traits). If the type of the dummy is a private class, the template will only be callable by a member.
template <ENU T>
struct protection_traits;
class B{
friend class protection_traits<T0>; // So it has access to Protected.
protected:
struct Protected{};
public:
struct Public{};
enum ENU{
T0,T1,T2
}
template<ENU T=T0> int f( typename protection_traits<T>::type = {})
{ std::cout<<"In enum "<<T<<std::endl; }
};
template <ENU T>
struct protection_traits
{
typedef B::Public type; // Default to public
};
template<>
struct protection_traits<T0>
{
typedef B::Protected type; // But T0 uses Protected
};
Usage:
B b;
int aa=b.f<T0>(); // fails (no access to B::Protected)
int bb=b.f<T1>(); // ok
Note: This latter solution hasn't been fed to a compiler. There will be typos.
I don't have a lot of experience with templates but I am wondering if the following scenario is possible. Assume we have a class S with static member stat. I can get it to dynamically generate different code using typeid as:
template <class S>
void foo()
{
if (typeid(S::stat) == typeid(AType))
implementation 1;
else
implementation 2;
}
But since all information is known at compile time is it possible to create a specialization of foo for S::stat being of type Atype?
You're probably looking to do something like this:
template<typename T> class foo_impl {
public:
static void foo()
{
// This is your implementation 2
}
};
template<> class foo_impl<AType> {
public:
static void foo()
{
// This is your implementation 1
}
};
template <class S>
void foo()
{
foo_impl<typename S::Stat>::foo();
}
One common way of solving this problem is through tag dispatching. We can, at compile time, produce different types for whether or not S::stat matches AType - and use those types to call different overloads:
template <class S>
void foo() {
foo_impl(std::is_same<decltype(S::stat), AType>{});
}
void foo_impl(std::true_type /* S::stat matches AType */) {
// implementation 1
}
void foo_impl(std::false_type /* S::stat doesn't match AType */) {
// implementation 2
}
I was not able to utilize the decltype solution because my compiler does not support it.
I was able to utilize the foo_impl class solution but only when I declare MyClass as:
class MyClass {
public:
typedef AType Stat;
static const Stat stat = VAL;
...
}
Looking at a simple template scenario like this:
class A {
public:
int work();
};
class B {
public:
int work();
};
class ObjectManager {
public:
static void manage( A& obj );
// manage not defined for class B
};
template<class T>
doStuff( T t ) {
t.work();
....
ObjectManager::manage(t);
};
A a;
B b;
doStuf(a);
doStuff(b);
I am wondering, what is the cleanest way to implement a conditional call to ObjectManager::manage? The template function should determine in compile-time if ObjectManager::manage(T t) is defined for the given T and activate some lines of code only when it is. I guess there are solutions with some more nested template calls, but it would be the best for me just to keep the function in one piece.
Note that currently your class members are all private. They should be made public.
template<class T>
auto call_manage(T* t) -> decltype(ObjectManager::manage(*t)){
return ObjectManager::manage(*t);
}
void call_manage(...) {}
template<class T>
void doStuff( T t ) {
t.work();
//....
call_manage(std::addressof(t)); // smack whoever overloaded unary
// operator & in the head first
}
If the expression ObjectManager::manage(*t) is well-formed, substitution succeeds for the function template, which is always a better match than ..., so it is called. Otherwise, the do-nothing overload is the only viable function.
Demo.
For those who like references:
template<class T>
auto call_manage(T& t) -> decltype(ObjectManager::manage(t)){
return ObjectManager::manage(t);
}
template<class... T>
void call_manage(T&&...) {}
template<class T>
void doStuff( T t ) {
t.work();
//....
call_manage(t);
}
In this case, the single-argument overload is more specialized than the variadic version by the partial ordering rules for function templates, and is selected by overload resolution if both are equally viable. You can also make it take T&& t and call manage(std::forward<T>(t)), for perfect forwarding.
Demo.
I have a template class, say:
template<class T>
class someClient
{
void someCallbackA() {foo_->onA();}
void someCallbackB() {foo_->onB();}
private:
T* foo_;
};
which I can instantiate with a bunch of different types T which support the onA and onB interface. I happen to have a case where two out of the several different types T I use needs a particular behavior controlled from someClient so I need to add some function doBar() to these two types (call them Edge1 and Edge2). Then I want a part of the someClient code to call foo_->doBar() but without breaking when the type of foo_ does not have that. Is there a way to use boost::enable_if to have a someClient::doBar() which will call foo_->doBar() only for those two types, but not be there, or expand to nothing if the types are not Edge1 or Edge2?
I was thinking along the lines of:
template <class T, enable_if<mpl_or<is_same<T,Edge1>, is_same<T,Edge2> > >
someClient<T>::doBar() {foo_->doBar();}
You don't need to pull any special tricks at all if you just don't call member functions that don't make sense. Template member functions are only specialized when needed (unless you add an explicit specialization). So the following code works fine:
template <typename T> struct Foo
{
void do_foo() { p->foo(); }
void do_bar() { p->bar(); }
T * p;
};
struct A { void foo() {} };
int main()
{
A a;
Foo<A> x = { &a };
x.do_foo();
}
The fact that Foo<A>::do_bar wouldn't compile is not an issue, since the member function is never instantiated. And p->bar isn't a compiler error, because p has a dependent type and the line is thus only parsed in the second lookup phase (which never happens).
I think this does what you want. I used C++11 <type_traits> instead of boost's:
struct Edge {
void doBar() { std::cout << "did Bar."; }
};
template<typename T>
class someClient
{
public:
template<typename U = T>
typename
std::enable_if<std::is_same<U, Edge>::value, void>::type
doBar() { foo_->doBar(); }
template<typename U = T>
void doBar( typename std::enable_if<!std::is_same<U, Edge>::value, void>::type* = 0 )
{ /* do nothing */ }
private:
T* foo_;
};
int main()
{
someClient<int> i;
someClient<Edge> e;
i.doBar();
e.doBar(); // outputs "did Bar."
}
doBar() needs to be template itself for this to work, explanation here: std::enable_if to conditionally compile a member function