4

Imagine this simple base class:

struct simple_http_service
{
  virtual reply http_get(…);
  virtual reply http_post(…);
  virtual reply http_delete(…);
  // etc.
};

I'd like to prevent the user from deriving from this class without overriding at least one of these and prevent them from instantiang simple_http_service

Is there some nice way to do this?

Karl von Moor
  • 8,484
  • 4
  • 40
  • 52
  • I am about 90% sure there isn't a really nice way of doing this, in particular it's hard to express constraints like do one of these in C++ – Aurojit Panda Jun 04 '11 at 09:33
  • 3
    Eh, seems like wonky design. If they can get away with ignoring two of three functions, what's one more? – GManNickG Jun 04 '11 at 09:37
  • 3
    If you are providing good default implementations of all of these functions why does it matter to you if someone wants to derived a class that uses them all? – CB Bailey Jun 04 '11 at 09:40
  • 2
    Still sounds like bad design. Let me ask this: what is the implementation of the other two functions if they're ignored? How is the user of the class aware of which function was actually overriden? – GManNickG Jun 04 '11 at 09:48
  • @GMan In case they are ignored by the service they will return status code 501 which means "not implemented". – Karl von Moor Jun 04 '11 at 10:11
  • 1
    I can think of a very good reason for instantiating either this class, or a derived class that doesn't override any of the functions: testing that all the functions behave correctly when not overridden. Why would you want to make that more difficult? – Mike Seymour Jun 04 '11 at 12:17

5 Answers5

5

That sounds like a really odd constraint. By all means protect the user from incorrect usage, but don't try to prohibit things that you just "can't see the point of". If there's no point in deriving from your class without overriding any of the three functions, then let the user override as many or as few function as he likes, and trust that he won't do the pointless thing of deriving without overriding any of the functions. There's no harm in the user doing that, it's just not very useful.

But if you do need to enforce this (again, I'd suggest you rethink), then don't use virtual functions. Instead, pass function pointers or function objects (or std::function/boost::function) callbacks. Make the base class look something like this:

struct simple_http_service
{
  typedef std::function<reply (...)> func_type;
  reply http_get(...) { return get_func(...); }
  reply http_post(...) { return post_func(...); }
  reply http_delete(...) { return delete_func(...); }
  // etc.

private:
  func_type get_func;
  func_type post_func;
  func_type delete_func;
};

Now just add the necessary constructors (or free/static functions so you can name them to avoid ambiguity) so that the class can only be instantiated when at least one of the function objects are supplied.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • 4
    +1 This is a really good approach that many will fail to grasp in a first pass. I use it and I recommend it. For casual readers: this means that you don't *implement* different services, but a *single* service, and during construction you can set it to dispatch to the handle for the operation. You can provide different *service* instances that will dispatch to different operations in the same class in the underlying model by using `std::bind` to control the dispatch, or you can dispatch to separate classes from a single *service*, this approach is much more flexible than inheritance – David Rodríguez - dribeas Jun 04 '11 at 10:17
1

I think all of these functions should be pure virtual. The struct you have posted is effectively an interface. If the functions are not all required, the derived structs should merely provide an empty implementation for the functions that are not relevant to them.

Gyan aka Gary Buyn
  • 12,242
  • 2
  • 23
  • 26
1

If you simply want to enforce the base class being abstract, give it a pure virtual destructor, and make your other functions ordinary virtual ones.

0

I don't not quite understand why do you want to provide default implementation for other two functions but require at least one of them to be user-defined in the case of http requests. It's clear if all functions are using each other to implement some functionality using existing code. Imagine the example with that class:

class Cls
{
  public:
    virtual std::string toString()=0;
    virtual std::string serialize()=0;
};

There is a class that is string-convertible and string-serializable. But if one of these is not implemented, you want to call the second instead, so that would be an option:

class Cls
{
  public:
    virtual std::string toString() //calls serialize() by default
    {
      return this->serialize();
    }
    virtual std::string serialize() //calls toString()
    {
      return this->toString();
    }
    virtual ~Cls()=0; //force the class to be abstract
};  Cls::~Cls(){}

But now there is the problem with deriving from Cls but not overriding at least one of the functions. If no overriding is made, at runtime you just enter infinite recursion. If this is one of your problems, there is a run-time solution, the code below is just not doing anything if such a problem occurs.

class Cls
{
  public:
    virtual std::string toString()
    {
      if ((void*)(this->*(&Cls::serialize)) != (void*)(&Cls::serialize))
      {//checks if the current implemetation is not equal to the default one
        return this->serialize();
      }
      else
      {
        return ""; //default return value
      }
    }

    virtual std::string serialize()
    {
      if ((void*)(this->*(&Cls::toString))!=(void*)((&Cls::toString)))
      {
        return this->toString();
      }
      else
      {
        return "";
      }
    }
    virtual ~Cls()=0;
};  Cls::~Cls(){}

This compiles on GCC, but fills your screen with warnings about strange convertion from funcptr to void*. At least it works as intended. There may be some metaprogramming compile-time solutions, need to think about it.

Appendix1, testing comparison between member funcs:

It is really weird

#include <iostream>

class Base
{
    public:
        virtual int test()
        {
            //default imp
            return 0;
        }
};

class Der : public Base
{
    public:
        int test() override
        {
            //custom imp
            return 1;
        }
};

int main()
{
    Der a;
    Base b;
    std::cout << ((&Der::test) == (&Base::test)) << std::endl;//1: wrong
    //they are not equal
    //but for some reason the output is "true"
    //so direct comparisons do not work
    //however
    //if you convert that pointer to void*
    //everything works
    std::cout << ((void*)(&Der::test) == (void*)(&Base::test) ) << std::endl;      //0:right
    std::cout << ((void*)(a.*(&Base::test)) == (void*)(&Base::test) ) << std::endl;//0:right
    std::cout << ((void*)(b.*(&Base::test)) == (void*)(&Base::test) ) << std::endl;//1:right
    std::cout << ((void*)(&(a.test)) == (void*)(&(b.test)) ) << std::endl;         //0:right
    //so if you want to compare two functions
    //cast them to void*
    //works in any cases
    //'-Wno-pmf-conversions' compiler flag to inhibit warnings about casting
    system("pause");
    return 0;
}

Appendix2, steps of getting real address of the function:

Cls::serialize; //the function object itself
&Cls::serialize; //its member pointer
(void*)(&Cls::serialize); //extracting real address of the function for the comparison
(this->*&Cls::serialize); //again, a member pointer
(void*)(this->*&Cls::serialize); //extracting real address
//  │        │  └── Getting "pointer" to a member function of the class
//  │        └───── Then binding 'this' to that function, recall that if the function is virtual, '->*' returns a mamber pointer to it's custom implementation, not the default one.
//  └────────────── Then getting the real address


// it looks like 'this->*&Cls::serialize' does the same as '&this->serialize'
// but in practice it's not quite right
// '&this->serialize' returns the function pointer based on 'this' type
// therefore, comparison breaks, as inside of a base class 'this' always has the base type
// so you always receive the default implementation pointer
// 'this->*&Cls::serialize' does the same
// but now if 'serialize' is virtual
// it takes it into account and sends back its' custom implementation pointer

// (void*) casting is required because you need to compare functions' real addresses
// if you compare member pointers of a single virtual function
// they seem to be equal while they are, in fact, not

The issue of checking if the derived class has implemented some of the base class virtual functions is here

drBright
  • 1
  • 1
0

If you know which methods that you want the deriving class to override, simply declare that method pure virtual.

For example, to make http_get a pure virtual:

struct simple_http_service
{
  virtual reply http_get(…) = 0;
  virtual reply http_post(…);
  virtual reply http_delete(…);
  // etc.
};
mdec
  • 5,122
  • 4
  • 25
  • 26
  • That's exactly my problem – I don't know which will be overriden, but I want to enforce to override at least one. – Karl von Moor Jun 04 '11 at 09:37
  • @Karl: "but I want to enforce to override at least one" Why? – GManNickG Jun 04 '11 at 09:38
  • @GMan Because I don't want it to be possible to instantiate the base class. I think I'll edit the question … – Karl von Moor Jun 04 '11 at 09:40
  • @Karl von Moor: Making it abstract would prevent it from being instantiated, but may not work with your design. – mdec Jun 04 '11 at 09:45
  • "I Don't want it to be possible" isn't a very good reason by itself. Where's the harm in it? Are you trying to protect against actual *errors*, or just trying to prevent operations that you can't see the point of? The latter is both annoying to the user, and fundamentally impossible. The former is useful and good practice – jalf Jun 04 '11 at 09:50
  • @jalf Why is it bad to keep things out which do not make any sense? – Karl von Moor Jun 04 '11 at 09:58
  • @Karl: Two reasons. First, you don't know yet which cases make sense. You may be surprised at how your library ends up being used, and people might find corner cases where it actually *makes sense* to do something you felt at design-time was pointless. And then the user is going to get pissed at you for tripping up the user for no actual benefit. And second, you'll be adding complexity to the library, for no actual benefit. Sensible programmers don't *need* to be told to do what's sensible. If something is pointless, they just won't do it. They don't need it to be prohibited by the compiler. – jalf Jun 04 '11 at 10:03