user10262178
user10262178

Reputation: 53

scalable SFINAE conditional override

Context

My situation is similar to the one described here: conditional (SFINAE) override.

Except in that question, there is only one function being conditionally overridden. I am wondering how I can get a solution that would enable this for n independent functions. For the answer described in the linked question, this would result in n^2 versions (true/false combinations) of the class.

Attempt

#include <cstdio>                                                                  
#include <type_traits>                                                             

struct A {                                                                         
    virtual void foo() { printf("A::foo()\n"); }                                   
    virtual void bar() { printf("A::bar()\n"); }                                   
    void print() { foo(); bar(); }                                                 
};                                                                                 

template <bool FOO, bool BAR>                                                      
struct B : public A {                                                              
    template <bool b = FOO>                                                        
    typename std::enable_if<b, void>::type                                         
    foo() override { printf("B::foo()\n"); }                                       

    template <bool b = BAR>                                                        
    typename std::enable_if<b, void>::type                                         
    bar() override { printf("B::bar()\n"); }                                       
};                                                                                 

int main() {                                                                       
    A *a = new B<true, false>();                                                   
    a->print();                                                                    

    return 0;                                                                      
}

However, g++-8.1.0 does not appear to generate symbols for the overridden methods and calls A::{foo,bar}():

$ g++ -std=c++11 test.cc && ./a.out && nm -C a.out | grep -e foo -e bar 
A::foo()
A::bar()
00000000004006de W A::bar()
00000000004006c4 W A::foo()

clang++-6.0 complains with:

error: only virtual member functions can be marked 'override'

They fixed a very similar bug: https://bugs.llvm.org/show_bug.cgi?id=13499. Ok, so I try without the override keyword and get the same result as g++:

$ clang++ -std=c++11 test.cc && ./a.out && nm -C a.out | grep -e foo -e bar
A::foo()
A::bar()
0000000000400850 W A::bar()
0000000000400820 W A::foo()

Both compilers failing to accomplish the task at runtime leads me to believe that I am doing something wrong with my example, or there is a rule that I am trying to break. Please enlighten me.

Goal

The goal is to have something that can override any combination of virtual methods in the base class without creating a "version" for each combination. Note: this should be done during compile-time.

Upvotes: 3

Views: 131

Answers (1)

max66
max66

Reputation: 66230

The problem is that a template function can't be a virtual one.

The best alternative that come in my mind is the creation of a oFoo template struct, with specialization, to override (or not) foo() according a boolean template value

template <bool>
struct oFoo : virtual public A
 { void foo () override { std::cout << "B::foo()" << std::endl; } };

template <>
struct oFoo<false>
 { };

then a oBar template struct for the same for bar()

template <bool>
struct oBar : virtual public A
 { void bar () override { std::cout << "B::bar()" << std::endl; } };

template <>
struct oBar<false>
 { };

so you can write B simply as follows

template <bool FOO, bool BAR>
struct B : virtual public A, public oFoo<FOO>, public oBar<BAR>
 { };

Observe the virtual inheritance to avoid the diamond problem.

The following is a full compiling example

#include <iostream>
#include <type_traits>                                                             
struct A
 {                                                                         
   virtual void foo() { std::cout << "A::foo()" << std::endl; }
   virtual void bar() { std::cout << "A::bar()" << std::endl; }
   void print() { foo(); bar(); }
 };

template <bool>
struct oFoo : virtual public A
 { void foo () override { std::cout << "B::foo()" << std::endl; } };

template <>
struct oFoo<false>
 { };

template <bool>
struct oBar : virtual public A
 { void bar () override { std::cout << "B::bar()" << std::endl; } };

template <>
struct oBar<false>
 { };

template <bool FOO, bool BAR>
struct B : virtual public A, public oFoo<FOO>, public oBar<BAR>
 { };

int main ()
 {
   A *a = new B<true, false>();

   a->print();
 }

Upvotes: 2

Related Questions