0

I have a pair of classes FS and DS which both derived from another class S. I have a function into which I pass in an S* (ie an FS or DS), which further calls another function passing in the same S*. In this final function I need to have different behaviour based on whether the S is an FS or DS, but would prefer to resolve this branch at compile time due to it being on a critical path. How can I achieve this? Something involving metaprogramming and or constexpr I feel - I don't want to be making a virtual call for each pass

Extraneous details omitted for brevity

class S {};
class FS : public S {};
class DS : public S {};

void func_2(Space *s) {
     // common code
     // branch based on lowest type of s
     // common code
}

void func_1(Space *s) {
    // common code
    func_2(s);
}
Madden
  • 1,024
  • 1
  • 9
  • 27
  • 3
    If you cannot know which concrete instance (FS or DS) the function will be invoked with then you cannot implement this type of *compile time polymorphism*, with or without templates. You either gonna have to switch it or use virtual calls indeed. – Geezer Aug 30 '18 at 10:27
  • See here for a summarizing answer regarding this type of template metaprogramming: https://stackoverflow.com/a/1881730/2754173 – Geezer Aug 30 '18 at 10:28
  • This question is quite unclear. Is this critical path being run more than once? Like in a loop? So does "pass" refer to a loop or something? Is it being run on the same object or type of object repeatedly? An [mcve] is required, where it is **both** minimal and **displays the problem**. – Yakk - Adam Nevraumont Aug 30 '18 at 17:34
  • @Yakk-AdamNevraumont It obviously isn't unclear or requires an MVP in this case considering 5 different users all replied with the more or less the same answer, 7 hours before you posted – Madden Aug 30 '18 at 20:48
  • 1
    @Madden I can think of a dozen answers that are different than the ones presented. They provided one possible answer to one possible problem that your question *could* be about. Is it about it? Maybe. The question is unclear. Unclear questions get answers, doesn't mean they are the answer to the real problem. – Yakk - Adam Nevraumont Aug 30 '18 at 20:50
  • If all you have in your array are S*, you have no choice but going virtual. Virtual calls within a loop should not be that expensive, as long as everything stays in cache for the duration, which it should. – Michaël Roy Sep 05 '18 at 19:18

2 Answers2

5

Both func_1 and func_2 should be templated, possibly with some SFINAE to make sure that calling types are derived from S. Then you just do if constexpr(std::is_same<std::decay_t<T>, FS>::value) and implement different behaviour for different classes.
If you pass base class pointer into the method, then I believe there is no way to retrieve the derived type at compile time.

Michael Veksler
  • 8,217
  • 1
  • 20
  • 33
paler123
  • 976
  • 6
  • 18
4

You can not do this at compile-time since the actual parameter given to the function and it's evaluation can happen only at run-time.

What you can do instead is moving the relationship between the types to compile-time, maybe you can read up on the Curiously recurring Template Pattern.

template <typename ActualType>
struct S  {};

struct DS:S<DS> {};

template <typename ActualType>
void func(S<ActualType>const& s)
{
    if constexpr(std::is_same<ActualType, DS>::value)
    //...
    else
    //...
}
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
Petok Lorand
  • 955
  • 6
  • 15
  • Good idea, I am already using CRTP for another family of classes but had hoped to solve this current problem without it – Madden Aug 30 '18 at 10:32
  • Instead of `template void func(S)` and then `if constexpr(std::is_same{})` you can use simple overloading: `void func(S)` and `void func(S)`. – Julius Aug 30 '18 at 11:59
  • 1
    In addition, I guess you meant to write `struct DS : S {};`. My [edit has been rejected](https://stackoverflow.com/review/suggested-edits/20728315). – Julius Aug 30 '18 at 20:17