views:

194

answers:

3

I have a method foo in class C which either calls foo_1 or foo_2. This method foo() has to be defined in C because foo() is pure virtual in BaseClass and I actually have to make objects of type C. Code below:

template <class T>
class C:public BaseClass{

  void foo() {
    if (something()) foo_1;
    else foo_2;

  }
  void foo_1() {
  ....
  }

  void foo_2() {
    ....
     T t;
     t.bar(); // requires class T to provide a method bar() 
     ....
   }
};

Now for most types T foo_1 will suffice but for some types foo_2 will be called (depending on something()). However the compiler insists on instantiating both foo_1 and foo_2 because either may be called.

This places a burden on T that it has to provide a bar method.

How do I tell the compiler the following:

  • if T does not have bar(), still allow it as an instantiating type?
+1  A: 

you could use boost.enable_if. something like this:

#include <boost/utility/enable_if.hpp>
#include <iostream>

struct T1 {
    static const bool has_bar = true;
    void bar() { std::cout << "bar" << std::endl; }
};

struct T2 {
    static const bool has_bar = false;
};

struct BaseClass {};

template <class T>
class C: public BaseClass {
public:
    void foo() {
        do_foo<T>();
    }

    void foo_1() {
        // ....
    }

    template <class U>
    void foo_2(typename boost::enable_if_c<U::has_bar>::type* = 0) {
        // ....
        T t;
        t.bar(); // requires class T to provide a method bar() 
        // ....
    }

private:

    bool something() const { return false; }


    template <class U>
    void do_foo(typename boost::enable_if_c<U::has_bar>::type* = 0) {
        if (something()) foo_1();
        else foo_2<U>();
    }

    template <class U>
    void do_foo(typename boost::disable_if_c<U::has_bar>::type* = 0) {
        if (something()) foo_1();
        // I dunno what you want to happen if there is no T::bar()
    }
};

int main() {
    C<T1> c;
    c.foo();
}
Evan Teran
@Evan: Can we do this without using boost ?
Please see http://stackoverflow.com/questions/2937425/boostenable-if-class-template-method/2937522#2937522 - you need to templatize "do_foo"
Johannes Schaub - litb
You are correct, I'm working on proper solution :-).
Evan Teran
@user231536: you could hand roll what boost::enable_if does, isn't that complex of a header. But I would just use that particular header. This example (now compiles!) will work for either `T1` or `T2` and will only attempt to call `bar` if the type claims to have it. Which seems like what you want.
Evan Teran
A: 

You could create an interface for foo_1 and foo_2, such as:


class IFoo
{
public:
    virtual void foo_1()=0;
    virtual void foo_2()=0;
};

template <typename T> 
class C : public BaseClass, public IFoo
{ 

  void foo()
  { 
    if (something())
        foo_1(); 
    else
        foo_2(); 
   } 
};

template <typename T>
class DerivedWithBar : C<T>
{
public:
    void foo_1() { ... } 

    void foo_2()
    { 
         ... 
         T t; 
         t.bar(); // requires class T to provide a method bar()  
         ...
     }
}; 

template <typename T>
class DerivedNoBar : C<T>
{
public:
    void foo_1() { ... } 
    void foo_2() { ... } 
};
andand
A: 

I think the easiest way is to simply write a separate function template that 'C' can call:

template <class T>
void call_bar(T& /*t*/)
{
}

template <>
void call_bar<Something>(Something& t)
{
    t.bar();
}

The original 'C' class can be modified accordingly:

void foo_2() {
  ....
   T t;
   call_bar(t); // does not require T to provide bar() 
   ....
}

This has the downside that you have to explicitly define which types of T provide a bar method, but that's pretty much inevitable unless you can determine something at compile-time about all the types that do provide a bar method in their public interface or modify all these bar-supporting types so that they do share something in common that can be determined at compile-time.

"This has the downside that you have to explicitly define which types of T provide a bar method, but that's pretty much inevitable unless you can determine something at compile-time about all the types that do provide a bar method in their public interface" you can force all of them to define such a function in their public interface. ADL [respects and finds](http://www.gotw.ca/publications/mill02.htm) functions defined in the namespaces of those types. See the link which explains the "Interface Principle". If i would go with a free function, i would base it on ADL.
Johannes Schaub - litb
@Johannes from what the OP said, something() is not an expression which can be evaluated at runtime. Without more contextual info, we have to assume that code calling both foo_1 and foo_2 (the one which expects bar) will be generated for all types, T. How do we take advantage of ADL to this case? Have a function, call_bar, that doesn't call bar in a separate namespace from the one which calls bar, put types with bar in a different namespace and depend on ADL to call the correct version?