tags:

views:

234

answers:

10

I would like to define as class X with a static method:

class X
{
 static string get_type () {return "X";}
 //other virtual methods
}

I would like to force classes which inherit from X to redefine the get_type() method and return strings different from "X" (I am happy if they just redefine get_type for now).

How do I do this? I know that I cannot have virtual static methods.

Edit: The question is not about the type_id, but in general about a static method that should be overriden. For example

class X {
 static int getid() {return 1;}
}
+1  A: 

Don't do that, use typeid instead.

pmr
I understand I can use typeid. I would like to rephrase the question about a general static method to be overriden by derived clases.
+1  A: 

To make a long story short, you can't do it. The only way to require a derived class to override a base class function is to make it a pure virtual (which can't be static).

Jerry Coffin
+1  A: 

You can't do this for a number of reasons. You can't define the function in X and have it be pure virtual. You can't have virtual static functions at all.

Why must they be static?

Bill
+5  A: 
template<int id>
class X {
public:
    static int getid() { return id; }
};

class Y : public X<2> {
};

You haven't overridden the method, but you've forced every subclass to provide an ID. Caveat: I haven't tried this, there might be some subtle reason why it wouldn't work.

Mark Ransom
Don't you still have to make sure each child uses a unique ID then?
Mark B
@Mark B, it's totally impossible for one class to enforce constraints on other classes it knows nothing about. They might be in completely different source files for example. The best you can do is get the programmer to think about it. Hopefully they won't follow this example: http://xkcd.com/221/
Mark Ransom
Nice! But the OP wanted `getid` to return strings. I suppose `getid` could return a key into a map of strings.
Emile Cormier
Isn't this prohibiting possible use of polymorphism? Every Y derives from a different type and there is no way to safely cast them?
pmr
@pmr, it might seem so, but you can always make another base class above X that does not depend on the template parameter.
Mark Ransom
@Mark, this feels like obfuscation just for the sake of design. While it kind of emulates the desired effect it isn't something that should be done. Good idea still.
pmr
+2  A: 

I'd say you know the why but just in case here's a good explanation:

http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlcpp8l.doc/language/ref/cplr139.htm

It looks like your going to have to design your way out of this. Perhaps a virtual function that wraps a Singleton?

David Relihan
+3  A: 

If I'm not mistaken, to call the static method, you have to invoke the method by specifying the exact name of the class, e.g X::get_type();, DerivedClass::get_type() etc and in any case, if called on an object, the dynamic type of the object is not taken into account. So at least in the particular case, it will probably only be useful in a templated context when you are not expecting polymorphic behavior.

However, I don't see why it shouldn't be possible to force each interesting class (inherited or not, since "compile-time polymorphism" doesn't care) to provide this functionality with templates. In the following case, you must specialize the get_type function or you'll have a compile-time error:

#include <string>

struct X {}; 
struct Derived: X {};

template <class T> std::string get_type() {
    static_assert(sizeof(T) == 0, "get_type not specialized for given type");
    return std::string(); 
}

template <> std::string get_type<X>() {
    return "X"; 
}

int main() {
    get_type<X>();
    get_type<Derived>(); //error 
}

(static_assert is C++0x, otherwise use your favourite implementation, e.g BOOST_STATIC_ASSERT. And if you feel bad about specializing functions, specialize a struct instead. And if you want to force an error if someone accidentally tries to specialize it for types not derived from X, then that should also be possible with type_traits.)

UncleBens
+1  A: 

Here you go

class X
{
  static string get_type() {return "X"; }
};

class Y : public X
{
  static string get_type() {return "Y"; }
};

The code above does exactly what you requested: the derived class redefines get_type and returns a different string. If this is not what you want, you have to explain why. You have to explain what is it you are trying to do and what behavior you expect from that static method. If is absolutely unclear form your original question.

AndreyT
"I would like to force classes which inherit from X to redefine the get_type() method and return strings different from X"...How does that do what he requested?
Michael Mrozek
@Michael Mrozek: What do you mean "how"? *Literally* and *precisely* - that's how! In my example above class `Y` redefines the method and returns a different string. Precisely as requested. Where do you see the problem?
AndreyT
@AndreyT: "I would like to **force** classes which inherit from X to redefine the get_type() method and return strings **different** from X". `string Y::get_type() {return "X";}` would work in your code. So would leaving `Y::get_type` undefined
Michael Mrozek
@Michael Mrozek: I don't believe the "forcing" is the key part of the question. To me the question looks like something about some "static polymorphism" in general. Although it is not clear what kind of polymorphism is required.
AndreyT
+1  A: 

You mention a few places about guaranteeing that the child types yield unique values for your function. This is, as others have said, impossible at compile time [at least, without the use of templates, which might or might not be acceptable]. But if you delay it until runtime, you can maybe pull something similar off.

class Base {
  static std::vector<std::pair<const std::type_info*, int> > datas;
  typedef std::vector<std::pair<const std::type_info*, int> >::iterator iterator;
public:
  virtual ~Base() { }
  int Data() const {
    const std::type_info& info = typeid(*this);
    for(iterator i = datas.begin(); i != datas.end(); ++i)
      if(*(i->first) == info) return i->second;
    throw "Unregistered Type";
  }

  static bool RegisterClass(const Base& p, int data) {
    const std::type_info& info = typeid(p);
    for(iterator i = datas.begin(); i != datas.end(); ++i) {
      if(i->second == data) {
    if(*(i->first) != info) throw "Duplicate Data";
    return true;
      }
      if(*(i->first) == info) throw "Reregistering";
    }
    datas.push_back(std::make_pair(&info, data));
    return true;
  }
};
std::vector<std::pair<const std::type_info*, int> > Base::datas;

class Derived : public Base { };
const DerivedRegisterFlag = Base::RegisterClass(Derived(), 10);

class OtherDerived : public Base { };
const OtherDerivedRegisterFlag = Base::RegisterClass(OtherDerived(), 10); //exception

Caveats: This is completely untested. The exceptions would get thrown before entering main if you do it this way. You could move the registration into constructors, and accept the per-instance overhead of registration checking if you'd rather.

I chose an unordered vector for simplicity; I'm not sure if type_info::before provides the necessary semantics to be used as a predicate for a map, and presumably you won't have so many derived classes that a linear search would be problematic anyhow. I store a pointer because you can't copy type_info objects directly. This is mostly safe, since the lifetime of the object returned by typeid is the entire program. There might be issues when the program is shutting down, I'm not sure.

I made no attempt to protect against static order of initialization errors. As written, this will fail at some point.

Finally, no it isn't static, but "static" and "virtual" don't really make sense together anyhow. If you don't have an instance of the type to act on, then how do you know which overwritten method to chose? There are a few cases with templates where you might legitimately want to call a static method without an actual object, but that's not likely to be common.

*edit: Also, I'm not sure how this interacts with dynamically linked libraries and the like. My suspicion is that RTTI is unreliable in those situations, so obviously this is similarly unreliable.

Dennis Zickefoose
+1  A: 

Use Delphi, it supports virtual static members on classes. ;>

dthorpe
A: 

Apologies for resurrecting this thread, but I've just encountered this moral crisis as well. This is a very bold and possibly foolish statement to make, but I wholeheartedly disagree with what most people are saying about static virtual not making any sense. This dilemma stems from how static members are commonly used versus what they're actually doing underneath.

People often express facts using static classes and/or members - something that is true for all instances if instances are relevant, or simply facts about the world in the case of static classes. Suppose you're modelling a Philosophy class. You might define abstract class Theory to represent a theory which is to be taught, then inherit from Theory in TheoryOfSelf, TheoryOfMind and so on. To teach a Theory, you'd really want a method called express() which expresses a theory using a particular turn of phrase appropriate to the audience. One would assume that any inheriting class should expose an identical method express(). If I were able to, I would model this relationship using static virtual Theory.express() - it is both a statement of fact transcending the concept of instances (therefore static) and nonspecific, requiring a specific implementation by each type of theory (therefore virtual).

I completely agree however with people justifying the prohibition on the grounds of what static is actually doing - it makes perfect sense in terms of coding principles, the issue arises from the customary ways people commonly model the real world.

The best resolution to this problem I've been able to think of is to model Theory as a singleton instead - there may be an instance of a theory, but there's only ever one of them. If you want an alternative, it's a different type, so create a new derived class. To me this approach just seems arbitrary and introduces unnecessary noise.

Tom W