simulating "static pure virtual" methods

Discussion in 'C++' started by cppaddict, May 24, 2004.

  1. cppaddict

    cppaddict Guest

    Hi,

    I know that it is illegal in C++ to have a static pure virtual method,
    but it seems something like this would be useful when the following 2
    conditions hold:

    1. You know that every one of your Derived classes will need to
    implement some method, but implement it differently, and that the base
    class cannot implement it. This is where pure virtual comes in.

    2. You know that for every one of your Derived classes, the method
    never uses or alters member data. Thus it should be static.

    To give a concrete example.

    Say you have an abstract Document class, which has a method called
    processDocument:

    void processDocument() {
    if (isApproved(_documentAuthor))
    callSomeNonVirtualFunction();
    else
    callAnotherNonVirtualFunction();
    }

    Just to take an easy example, let's say that we know isApproved() is
    some silly function of the author's name, which is passed in as an
    arguement. Maybe for a derived KinkosDocument, isApproved() returns
    true if the author's name begins with "A", and is otherwise false.
    For some other derived class the rule would be different, but always a
    function of the author's name itself.

    So we have the following:

    1. isApproved() should not be implemented in the base Document class.

    2. isApproved() must be implemented in any derived concrete class.

    3. isApproved() must be a simple function of a string.

    What is the right design for a situation like this?

    Thanks,
    cpp
    cppaddict, May 24, 2004
    #1
    1. Advertising

  2. cppaddict

    Sharad Kala Guest

    "cppaddict" <> wrote in message
    news:...
    > Hi,
    >
    > I know that it is illegal in C++ to have a static pure virtual method,
    > but it seems something like this would be useful when the following 2
    > conditions hold:
    >

    Perhaps you need something like this -

    struct A{
    virtual void foo() =0;
    };

    struct B : A{
    void foo(){
    staticB();
    }
    static void staticB(){}
    };

    struct C : A{
    void foo(){
    staticC();
    }

    static void staticC(){}
    };

    int main()
    {
    }

    -Sharad
    Sharad Kala, May 24, 2004
    #2
    1. Advertising

  3. cppaddict wrote:
    >
    > Hi,
    >
    > I know that it is illegal in C++ to have a static pure virtual method,
    > but it seems something like this would be useful when the following 2
    > conditions hold:
    >
    > 1. You know that every one of your Derived classes will need to
    > implement some method, but implement it differently, and that the base
    > class cannot implement it. This is where pure virtual comes in.
    >
    > 2. You know that for every one of your Derived classes, the method
    > never uses or alters member data. Thus it should be static.
    >
    > To give a concrete example.
    >
    > Say you have an abstract Document class, which has a method called
    > processDocument:
    >
    > void processDocument() {
    > if (isApproved(_documentAuthor))
    > callSomeNonVirtualFunction();
    > else
    > callAnotherNonVirtualFunction();
    > }
    >
    > Just to take an easy example, let's say that we know isApproved() is
    > some silly function of the author's name, which is passed in as an
    > arguement. Maybe for a derived KinkosDocument, isApproved() returns
    > true if the author's name begins with "A", and is otherwise false.
    > For some other derived class the rule would be different, but always a
    > function of the author's name itself.


    plus - and this is important - the type of Document which should
    check the name. So in the above you simply cannot have a call
    to isApproved only. It needs to be some class member. But then
    you need an object of that class to determine the dynamic
    type of the Derived class and hence isApproved can be
    a normal virtual member function of that class.
    There is nothing a static method can do better or worse
    in this case. You still need an object to call the method
    to identify the context in which the call should be made.

    Static functions are usefull if you don't want to specify
    any context but use the class as a sort of 'namespace', to avoid
    a free standing function. In your example the function cannot
    be freestanding, thus static isn't of any use to this problem.


    --
    Karl Heinz Buchegger
    Karl Heinz Buchegger, May 24, 2004
    #3
  4. cppaddict

    Jeff Schwab Guest

    cppaddict wrote:
    > Hi,
    >
    > I know that it is illegal in C++ to have a static pure virtual method,
    > but it seems something like this would be useful when the following 2
    > conditions hold:
    >
    > 1. You know that every one of your Derived classes will need to
    > implement some method, but implement it differently, and that the base
    > class cannot implement it. This is where pure virtual comes in.
    >
    > 2. You know that for every one of your Derived classes, the method
    > never uses or alters member data. Thus it should be static.
    >
    > To give a concrete example.
    >
    > Say you have an abstract Document class, which has a method called
    > processDocument:
    >
    > void processDocument() {
    > if (isApproved(_documentAuthor))
    > callSomeNonVirtualFunction();
    > else
    > callAnotherNonVirtualFunction();
    > }
    >
    > Just to take an easy example, let's say that we know isApproved() is
    > some silly function of the author's name, which is passed in as an
    > arguement. Maybe for a derived KinkosDocument, isApproved() returns
    > true if the author's name begins with "A", and is otherwise false.
    > For some other derived class the rule would be different, but always a
    > function of the author's name itself.
    >
    > So we have the following:
    >
    > 1. isApproved() should not be implemented in the base Document class.
    >
    > 2. isApproved() must be implemented in any derived concrete class.
    >
    > 3. isApproved() must be a simple function of a string.
    >
    > What is the right design for a situation like this?


    There's no need for the base class to declare the method. The point of
    virtual methods is to allow subclass-specific behavior through pointers
    (or references) to objects of a base type. You can't have a pointer to
    an entire class, so there's no point in having a static virtual method.

    If client code expects some feature of a class, and that feature (e.g. a
    static method with a particular name) is missing, you will get a
    compile-time error.

    If you want to ensure a priori that each of a group of types can be used
    in particular ways, you can write a "constraints" class to express the
    requirements. You do have to instantiate the class for each type you
    want to check explicitly. If you find this approach useful, check out
    the Boost Concept Checking library.

    struct A
    {
    // Each subclass should have a default constructor, and a static
    // method called "foo" that can be called with no arg's.
    //
    // Instantiate A::Check_constraints for each type derived from A
    // to check these requirements.

    template< typename T >
    struct Check_constraints
    {
    static void check_for_default_constructor ( )
    {
    T t = T( );
    }

    static void check_for_static_method_foo( )
    {
    T::foo( );
    }
    };
    };

    struct B: A
    {
    static void foo( ) { }
    B ( ) { }
    };

    struct C: A
    {
    C ( ) { }
    };

    /* Explicit instantiations verify that B and C meet A's requirements.
    */
    template class A::Check_constraints< B >;
    template class A::Check_constraints< C >;
    Jeff Schwab, May 24, 2004
    #4
  5. cppaddict

    jeffc Guest

    "cppaddict" <> wrote in message
    news:...
    > Hi,
    >
    > I know that it is illegal in C++ to have a static pure virtual method,
    > but it seems something like this would be useful when the following 2
    > conditions hold:
    >
    > 1. You know that every one of your Derived classes will need to
    > implement some method, but implement it differently, and that the base
    > class cannot implement it. This is where pure virtual comes in.
    >
    > 2. You know that for every one of your Derived classes, the method
    > never uses or alters member data. Thus it should be static.


    Why should it be static just because of that? If it's static, it can't be
    virtual. Virtual is what allows overriding.
    jeffc, May 24, 2004
    #5
  6. cppaddict

    Andy Venikov Guest

    cppaddict <> wrote in message news:<>...
    > Hi,
    >
    > I know that it is illegal in C++ to have a static pure virtual method,
    > but it seems something like this would be useful when the following 2
    > conditions hold:
    >
    > 1. You know that every one of your Derived classes will need to
    > implement some method, but implement it differently, and that the base
    > class cannot implement it. This is where pure virtual comes in.
    >
    > 2. You know that for every one of your Derived classes, the method
    > never uses or alters member data. Thus it should be static.
    >
    > To give a concrete example.
    >
    > Say you have an abstract Document class, which has a method called
    > processDocument:
    >
    > void processDocument() {
    > if (isApproved(_documentAuthor))
    > callSomeNonVirtualFunction();
    > else
    > callAnotherNonVirtualFunction();
    > }
    >
    > Just to take an easy example, let's say that we know isApproved() is
    > some silly function of the author's name, which is passed in as an
    > arguement. Maybe for a derived KinkosDocument, isApproved() returns
    > true if the author's name begins with "A", and is otherwise false.
    > For some other derived class the rule would be different, but always a
    > function of the author's name itself.
    >
    > So we have the following:
    >
    > 1. isApproved() should not be implemented in the base Document class.
    >
    > 2. isApproved() must be implemented in any derived concrete class.
    >
    > 3. isApproved() must be a simple function of a string.
    >
    > What is the right design for a situation like this?
    >
    > Thanks,
    > cpp


    Static functions can't be virtual for a reason. You use them
    when you want to execute something class-specific without any
    context, meaning without an object of that class. Virtual functions
    make no sense without objects. In other words, to call a virtual
    function, you need an object. But if you have an object, you don't
    need the function to be static.

    If what you want is for for each class to have it's own
    implementation of isApproved, but be called not through the object
    invocation, but through a class invocation, then you can achieve that by
    just overloading the isApproved. The derived class's isApproved will
    hide the base class's isApproved when you do this: Derived::isApproved.
    The problem here is now you'll have to duplicate processDocument() in
    each derived class, because parent's processDocument will only call
    parent's isApproved().

    If you make isApproved a public function, then you can use
    a template function like this:

    template <class T> void processDocument(documnetAuthor)
    {
    if (T::isApproved(documentAuthor))
    T::SomeNonVirtualF;
    else
    T::SomeOtherNonVirtualF;
    }

    and call it like this

    processDocument<DerivedDoc>(documentAuthor);

    Andy.
    Andy Venikov, May 24, 2004
    #6
  7. Andy Venikov wrote:
    > cppaddict <> wrote in message news:<>...
    > > [...]

    >
    > Static functions can't be virtual for a reason. You use them
    > when you want to execute something class-specific without any
    > context, meaning without an object of that class. Virtual functions
    > make no sense without objects. In other words, to call a virtual
    > function, you need an object. But if you have an object, you don't
    > need the function to be static.
    > [...]


    Andy, take a look at the recent thread in comp.std.c++ about static
    virtual functions (titled "static member functions and 'virtual'",
    started by David Whipp) and the explanation why they can be of use,
    perhaps you will not be as rigid after that.

    Victor
    Victor Bazarov, May 24, 2004
    #7
  8. cppaddict

    cppaddict Guest

    Karl,

    Thanks for your reply. I've got a couple more questions though...

    >Static functions are usefull if you don't want to specify
    >any context but use the class as a sort of 'namespace', to avoid
    >a free standing function. In your example the function cannot
    >be freestanding, thus static isn't of any use to this problem.


    I think my point is that my the function isApproved() could just as
    well be freestanding. It just processes a string passed to it and
    returns true/false. The only reason that I don't want it to be
    freestanding is that the only place I'm using it is in this class, and
    I want to make that connection clear by having it be a member
    function. Thus your description of static functions only being useful
    as a sort of 'namespace' applies perfectly here. It's just that I
    also want to say syntactically: "This function will be implemented by
    all derived classes."

    To give you an idea of my priorities, right now my solution is simply
    to have isApproved be a virtual function, and ignore the fact that it
    doesn't really require any context. Indeed, to give it context, I
    could have it operate on _documentAuthor directly instead of passing
    that in as an argument. Then I would no longer feel that it "should"
    be static. Perhaps that is the real issue here....

    Thanks for any further thoughts,
    cpp
    cppaddict, May 24, 2004
    #8
  9. I think in this example you'd be better off using a "strategy" design
    pattern
    or something else like policy classes.

    In the strategy pattern approach, you will have a data member which
    provides the IsApproved() method. You can construct the class by
    passing such an approval object as a parameter to the constructor.
    In this way, the workings of the class can be left alone, the approval
    object provides the variations in the approval. This is good if all that
    is varying is the approval behavior, not the document behavior.


    for example:

    class Document
    {
    public:
    Document( Approver* approver );

    };

    class Approver
    {
    public:
    virtual bool isApproved()=0;
    };

    class KinkoApprover
    {
    public:
    virtual bool isApproved () { ....do approval ...}
    };
    class OfficeDepotApprover
    {
    public:
    virtual bool isApproved () { ....do approval ...}
    };

    Document* kinkoDoc = new Document( new KinkoApprover() );
    Document* officeDepotDoc = new Document( new OfficeDepotApprover() );


    dave


    "cppaddict" <> wrote in message
    news:...
    > Hi,
    >
    > I know that it is illegal in C++ to have a static pure virtual method,
    > but it seems something like this would be useful when the following 2
    > conditions hold:
    >
    > 1. You know that every one of your Derived classes will need to
    > implement some method, but implement it differently, and that the base
    > class cannot implement it. This is where pure virtual comes in.
    >
    > 2. You know that for every one of your Derived classes, the method
    > never uses or alters member data. Thus it should be static.
    >
    > To give a concrete example.
    >
    > Say you have an abstract Document class, which has a method called
    > processDocument:
    >
    > void processDocument() {
    > if (isApproved(_documentAuthor))
    > callSomeNonVirtualFunction();
    > else
    > callAnotherNonVirtualFunction();
    > }
    >
    > Just to take an easy example, let's say that we know isApproved() is
    > some silly function of the author's name, which is passed in as an
    > arguement. Maybe for a derived KinkosDocument, isApproved() returns
    > true if the author's name begins with "A", and is otherwise false.
    > For some other derived class the rule would be different, but always a
    > function of the author's name itself.
    >
    > So we have the following:
    >
    > 1. isApproved() should not be implemented in the base Document class.
    >
    > 2. isApproved() must be implemented in any derived concrete class.
    >
    > 3. isApproved() must be a simple function of a string.
    >
    > What is the right design for a situation like this?
    >
    > Thanks,
    > cpp
    >
    Dave Townsend, May 24, 2004
    #9
  10. cppaddict

    cppaddict Guest


    >In the strategy pattern approach, you will have a data member which
    >provides the IsApproved() method. You can construct the class by
    >passing such an approval object as a parameter to the constructor.
    >In this way, the workings of the class can be left alone, the approval
    >object provides the variations in the approval. This is good if all that
    >is varying is the approval behavior, not the document behavior.


    Dave,

    Thanks for that. That would work here and seems appropriate. But
    how, in general, does one decide when a Strategy Pattern is
    appropriate and when a virtual function is appropriate?

    Both methods seem to allow you to separate out implementation from
    context.

    Thanks again,
    cpp
    cppaddict, May 24, 2004
    #10
  11. CPP,

    You might want to read the chapter in Gof Design Patterns to
    review the pros and cons of a strategy, they probably explain
    things much better than I can here. Also Martin Fowler's book
    on Refactoring I believe illustrates this idea quite well.

    One point, the introduction of the strategy pattern doesn't necessarily
    mean the elimination of the virtual function in the document class :

    virtual bool isApproved( ){ return _approver->isApproved( authorName) ;};

    Here the approval call is just forwarded to the strategy object, so the
    public interface to the class would be the same as in your original idea.


    I think in your situation where you potentially could have a lot of
    different "approval" strategies but only one real document class,
    the strategy is the way to go. In addition, new approver objects can
    be introduced without changing the document class. Also, the document
    base class can be further derived and used with these or other strategies.

    Another consideration would if other strategies could be factored out
    of your document class, so you are left with a skeletal class with
    a bunch of different strategies which do the real work. For instance,
    document
    persistence could be handled through another strategy object, so you could
    "persist" to a memory
    stream or file medium. The strategy approach makes this more flexible,
    so you could have a memory-stream-kinko document as well as a file-persist
    office-depot document etc. This would be too awkward if you have to
    declare/define classes for every variation. Now introduce another variation
    - UpperCase document/LowerCase - ( I know its a bit contrived but I'm trying
    to illustrate a point.)

    I tend to use strategy patterns a lot, its my favourite! Sometimes my
    strategy object itself is
    extrodinarily very simple and designed for only one particular purpose, I
    can write
    a simple object correctly, but I might goof on a more complex one!

    Hope that helps!

    dave


    "cppaddict" <> wrote in message
    news:...
    >
    > >In the strategy pattern approach, you will have a data member which
    > >provides the IsApproved() method. You can construct the class by
    > >passing such an approval object as a parameter to the constructor.
    > >In this way, the workings of the class can be left alone, the approval
    > >object provides the variations in the approval. This is good if all that
    > >is varying is the approval behavior, not the document behavior.

    >
    > Dave,
    >
    > Thanks for that. That would work here and seems appropriate. But
    > how, in general, does one decide when a Strategy Pattern is
    > appropriate and when a virtual function is appropriate?
    >
    > Both methods seem to allow you to separate out implementation from
    > context.
    >
    > Thanks again,
    > cpp
    Dave Townsend, May 25, 2004
    #11
  12. cppaddict

    Andy Venikov Guest

    > Andy, take a look at the recent thread in comp.std.c++ about static
    > virtual functions (titled "static member functions and 'virtual'",
    > started by David Whipp) and the explanation why they can be of use,
    > perhaps you will not be as rigid after that.
    >
    > Victor


    Victor,
    I'm aware of the issue. In fact, I was trying to explain it and a
    possible solution in my reply to the OP.

    <Snippet>:
    >The problem here is now you'll have to duplicate processDocument() in
    >each derived class, because parent's processDocument will only call
    >parent's isApproved().


    >If you make isApproved a public function, then you can use
    >a template function like this:


    >template <class T> void processDocument(documnetAuthor)
    >{
    > if (T::isApproved(documentAuthor))
    > T::SomeNonVirtualF;
    > else
    > T::SomeOtherNonVirtualF;
    >}


    >and call it like this


    >processDocument<DerivedDoc>(documentAuthor);

    </End snippet>

    As you can see, the solution I came up with is exactly the same
    Alberto Barbatti used in his post. It won't work if we need to
    call non-public functions though. I'm still thinking about a
    way to fix that.
    What we need is sort-of "base template function". Template
    function defined in the base that is instanciated in each
    derived class with that class's type.

    Thanks though,
    Andy.
    Andy Venikov, May 26, 2004
    #12
  13. cppaddict

    Andy Venikov Guest

    >I'm still thinking about a
    >way to fix that.
    >What we need is sort-of "base template function". Template
    >function defined in the base that is instanciated in each
    >derived class with that class's type.


    Here's a possible solution. It looks ugly though.
    The idea is to have in each class a static pointer
    to function initialized with an instatiation of a base
    class's template function parameterized with class's type.

    Here goes:

    struct Base
    {
    template <class T>
    static void Func1()
    {
    cout << "template<> Func1 called." << endl;
    T::Func2();
    }

    static void (*func1)();

    static void Func2()
    {
    cout << "Base::Func2()" << endl;
    }
    };

    void (*Base::func1)() = &Base::Func1<Base>;


    struct Derived : Base
    {
    static void Func2()
    {
    cout << "Derived::Func2()" << endl;
    }

    static void (*func1)();
    };
    void (*Derived::func1)() = &Base::Func1<Derived>;

    int main(int argc, char ** argv)
    {
    Base::func1();
    Derived::func1();

    return 0;
    }


    Cheers,
    Andy.
    Andy Venikov, May 26, 2004
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. DG
    Replies:
    3
    Views:
    452
  2. Modukuri
    Replies:
    7
    Views:
    3,174
    Marcus Harnisch
    Jun 1, 2004
  3. Johannes Zellner
    Replies:
    1
    Views:
    501
    Alex Martelli
    Jan 17, 2006
  4. jpr
    Replies:
    2
    Views:
    330
    red floyd
    Mar 25, 2006
  5. Kenneth McDonald
    Replies:
    5
    Views:
    314
    Kenneth McDonald
    Sep 26, 2008
Loading...

Share This Page