2

Is it possible to write a template function that would possess type information about the base class of the template argument? (assuming that the template argument derives from one class only)

So, I am looking for something like this:

template <class T> 
auto f(T t) -> decltype(...) { // ... is some SFINAE magic that 
                               //     catches B, the base of T
    std::cout << (B)t << std::endl;
}  

EDIT: Some relevant background. I am writing a generic implementation of the A* algorithm. The template argument is a Node structure. So, the user might define:

struct NodeBase {
    REFLECTABLE((double)g, (double)f)
        // Using the REFLECTABLE macro as described here:                    
        // http://stackoverflow.com/a/11744832/2725810 
};

struct NodeData : public NodeBase {
    using Base1 = NodeBase;
    REFLECTABLE((double)F)
};

I would like to write a function that prints the contents of the node structure. REFLECTABLE does all the hard work of extracting the fields of the struct. However, when the user gives me a NodeData instance, my function needs to print the contents of the NodeBase component as well. I would like to later add overloads of my function for two and three base classes.

AlwaysLearning
  • 7,257
  • 4
  • 33
  • 68

2 Answers2

1

The SFINAE stuff you are looking for is in general not possible. What would you do for multiple inheritance? Private/protected inheritance? And I bet there are also more sophisticated problems I haven't thought about yet. The only way I see is by consistently using some reflection library (and not just a single macro), in which you can explicitly define the result you want to obtain.

Instead, the non-SFINAE and non-exciting way you might be looking for is to simply overload your function once for NodeBase and then pass the derived type via (const-)reference:

auto f(NodeBase const& t)
{
    std::cout << t << std::endl;
} 

The call f(NodeData{}) will then automatically get a reference to the base class NodeBase. Note that the reference is important here, otherwise you're slicing the derived object.

davidhigh
  • 14,652
  • 2
  • 44
  • 75
0

If you know what are the types from which your classes can inherit, here is a method to dig all the actual base classes out for each instance.
It follows a minimal, working example:

#include<type_traits>
#include<iostream>

template<int i>
struct check: check<i+1> { };

template<>
struct check<3> { };

struct B1 { };
struct B2 { };
struct B3 { };

struct D: B1, B3 { };

template<class T>
typename std::enable_if<std::is_convertible<T, B1>::value>::type
f(check<0>, T t) {
    B1 b1 = static_cast<B1>(t);
    std::cout << "B1" << std::endl;
    f(check<1>{}, t);
}

template<class T>
typename std::enable_if<std::is_convertible<T, B2>::value>::type
f(check<1>, T t) {
    B2 b2 = static_cast<B2>(t);
    std::cout << "B2" << std::endl;
    f(check<2>{}, t);
}

template<class T>
typename std::enable_if<std::is_convertible<T, B3>::value>::type
f(check<2>, T t) {
    B3 b3 = static_cast<B3>(t);
    std::cout << "B3" << std::endl;
    f(check<3>{}, t);
}

template<class T>
void f(check<3>, T) {
    std::cout << "end" << std::endl;
}

template <class T> 
void f(T t) {
    f(check<0>{}, t);
}

int main() {
    D d;
    f(d);
}

Note how it skips the function involving B2 because it is not a base type for D.
This method is also easy to extend if you have more than 3 types from which to inherit, it doesn't force fixed inheritance chains and you can perform different operations on different types.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • I think you mean `std::is_base_of` rather than `std::is_convertible`. `std::is_convertible::value` will be `true` for example, if `T` has an overloaded cast operator to `U`. – Codie CodeMonkey May 31 '18 at 07:12