views:

431

answers:

6

Hi,

In my application I have to derive some classes from a base one, the problem is that I want to enforce the derived classed to have 3 particular constructor implementation. As c++ don't have virtual pure constructor, it seemed quite desperate (I had to check manually each class implementation to ensure that the particular ctors are implemented, not quite fun to do).

Yesterday I found a crazy way to simulate the comportment of a virtual ctor:



template <class T>
class AbstractEnforcer{
  protected:
    AbstractEnforcer(){}
  private:
    static void Enforcer(){
      delete new T();
      delete new T(*(new unsigned int));
      delete new T(*(new unsigned int, *(new QString));
    }
  }

class AbstractClass : private AbstractEnforcer<AbstractClass>{

}

the only inconvenience with this approach is that I have to to declare all derived class with the syntax:


class X : private AbstractEnforcer<X>

And even if it's not a problem; because the Enforcer() methode never get called (and even so it does nothing [hope so!!!])

My question is: "is there any mean (not with macros) to force the derived class to use this mechanism without parametrazing the AbstractClass (because this will work in only one level of derivation"


template <class T>
class AbstractClass : private AbstractEnforcer<T>{

}
+3  A: 

Your solution does not solve the problem since templated code that isnt used is not instantiated, and thus unless you manually invoke this function - it will not verify the existence of the wanted constructors.

What you can do is have this method called from the constructor of your enforcer:

template <class T>
class AbstractEnforcer{
    protected:
        AbstractEnforcer(){ Enforcer(); }
    private:
        static void Enforcer(){
            delete new T();
            delete new T(*(new unsigned int));
            delete new T(*(new unsigned int, *(new QString)));
        }
        // disable:
        AbstractEnforcer (const AbstractEnforcer &enf);
};

class AbstractClass : private AbstractEnforcer<AbstractClass>{

};

int main () {
    AbstractClass c;
}

Then, the compiler complains - mission accomplished.

Note that I've disabled the copy constructor so that there will be no way to bypass that check (by calling a different constructor).

Edit - Non leaking Enforcer(): [As there's absolutly no need to use dynamic allocations there..]

        static void Enforcer(){
            T t1();
            T t2(int(3));
            T t3(int(4), QString());
        }
rmn
in reality i defined the enforcer function in a different cpp file and the static constructor check is working quite good, but I admit this is a better way to do it (I'm using gcc-4.x) and don't know if this works with other compilers.
chedi
The problem is when I want to derive a class a from AbstractClass, the compiler don't enforce the prototype checking since AbstractClass inherits AbstractEnforcer<AbstractClass>::Enforcer() and there is no automatic mean to do a check on AbstractEnforcer<a>
chedi
You'll have to inherit in every new class I'm afraid, since this function must attempt to invoke the constructors of every class, and not only the constructors of the directly inheriting from it. But it will work just fine.
rmn
I totaly orget to tel you that I noticed that if you call the Enforcer method from within the constructor you create an infinite loop and and a stackoverflow (hi hi).because the first instrction in the Enforcer method is calling the default ctor, which will call the Enforcer method again and so on
chedi
I see. Well, you can have a static boolean flag to change using RAII so you set it at the first entry and unset at leaving, and only try these things if its not set.. then you'll fix the recursion problem. Also, another suggestion is to use automatic variables invoking the needed constructors instead of this dynamic heap allocation.
rmn
Note that gcc 4.something does all compile time checks on templated code, which is a good thing in many cases! It helps detecting errors early. I think on gcc the method works (though, I didn't validate this).
frunsi
A small detail: Your code calls Enforcer(), which leaks ints and QStrings.
Joh
Joh - thanks for the heads up. As I said earlier though, there's no need to use dynamic allocation there at the first place. Edited now, cheers.
rmn
+3  A: 

See this page in the C++ FAQ.

What I would do is something like this:

class AbstractClass {
    public:
        virtual AbstractClass* create() const = 0;
        virtual AbstractClass* create(unsigned int) const = 0;
        virtual AbstractClass* create(unsigned int, QString) const = 0;
};

Then each of the derived classes would be forced to override these functions, which should create new objects with different constructors.

jhoyt
I knew that we can't have a virtual constructor and I don't want to have one, my purpose is to a compiler static code checking that will alert me if I forgot to implement a specific constructor prototype.My project is a plugin like dynamic loading system and I have in some way enforce the ctors prototypes implementation of the third party code.
chedi
@chedi: I don't get what you are trying to achieve here. Why do constructors NEED to be those three? It seems more reasonable (common an idiomatic) to provide a factory in the plugins that take those same arguments (which is close to what jhoyt has posted). How do you intend to instantiate in your code an object of a type unknown at compile time?
David Rodríguez - dribeas
I second dribeas comment. Your idea will not work, because the plugin host has no way to construct objects that are unknown of compile time. You need a factory (e.g. a list of `Object* CreateObject(...)` functions, that the plugin must export). You will also make your life easier by requiring a C calling convention on the exports (the returned objects may be C++ of course), C++ plugins are tricky.
frunsi
A: 

If you forget to implement the constructor but use it in your code, you will get a compilation error. For example:

Base * p = new Derived( 42 );

will be a compile-time error if the Derived(int) constructor is not provided - the Base constructor will not be used.

anon
+1  A: 

I'd probably just have a template for generating a test:

template <typename T>
void enforceConstructors() {
    T t1;
    T t2(0);
    QString q;
    T t3(0, q);
}

Then somewhere in your tests, do:

enforceConstructors<X>();
enforceConstructors<Y>();
enforceConstructors<Z>();

Those might be all together, or in separate places for each of the classes X, Y, Z. Depends how you want to organise your tests.

If the values I used aren't suitable, either put in some values which are, or else compile that test but don't run it. If you don't have unit tests either get some, or else add the following to the class (instead of inheriting from the base class):

#ifndef NDEBUG
    static void test() { enforceConstructors<X>(); }
#endif

You don't usually need to make constructors part of an interface defined by an abstract base class. The reason is that such interfaces are for dynamic polymorphism - you pass an object to some function, and it calls functions on it. You can't "pass" a class to a function and have it instantiate the class other than with templates. Templates mostly enforce their interfaces at compile time anyway - if you instantiate the template and it uses the constructor, then the constructor has to be there.

Steve Jessop
You might want to add to your example to enforce on classes Y and Z to show how to expand what you want.Also, I'd suggest making *static void test()* into a non-static and more obscure class, such as: *extern void constructorEnforcer()* on the unlikely chance that the compiler optimized it away since a static unused in the file is dead code.
NVRAM
It doesn't matter whether the compiler optimises it away. It still has to ensure that it's legal, because it's a real function. At least, I'm pretty sure that calling an undefined function from another function requires a diagnostic in C++ (template code being an exception to that rule, as long as it's not instantiated and the function called is dependent on a template parameter). Here the template must be instantiated, even if `test()` is somehow removed as dead code at a later point. C++ doesn't allow dead code not to be compiled, just allows it to be removed later.
Steve Jessop
Come to think of it, this code ensures that the relevant constructors are *declared*. If the constructors are declared but not defined, then this code might not detect that problem, if you never link any code that actually executes the the test. I'm not sure which we're supposed to be doing - in any case it's best just to figure out some parameters that work, and run the tests.
Steve Jessop
+1  A: 

From this comment to one of the answers I think that you do not really want to achieve what you are asking here, but rather a different thing. The comment I refer to is:

Part 1:

I knew that we can't have a virtual constructor and I don't want to have one, my purpose is to a compiler static code checking that will alert me if I forgot to implement a specific constructor prototype.

Part 2:

My project is a plugin like dynamic loading system and I have in some way enforce the ctors prototypes implementation of the third party code.

What you are asking in the question is 1, and you can enforce it in different ways, just read some of the answers, or take a look at metaprogramming examples and the boost::type_traits library.

Now, if what you really want is part 2: provide a dynamic loading plugin mechanism then you do not need to enforce the constructors, but a common interface both for the plugin objects and the creation of the plugin objects. There is no way of instantiating an instance of an object of unknown (at compile time) object, and that means that you will not be able to call the constructors from your code. I would suggest

// Interface:
class plugin {
public:
   virtual void operation() = 0;
};
class plugin_factory {
public:
   virtual plugin* create() = 0;
   virtual plugin* create( unsigned int ) = 0;
   virtual plugin* create( unsigned int, QString const & ) = 0;
};

Users will need to provide the implementation of a plugin and the factory that creates them. They will probably need to implement a point of entry for their library so that you can get access to the factory (or so they can register their factory within your system, or else I would suggest using a library for these purposes (boost::extension seems like a place to look at)

David Rodríguez - dribeas
A: 

I finally adopted this solution, but not quit convienced:


#ifdef NDEBUG

#ifndef ENFORCE_CTORS
#define ENFORCE_CTORS(enforcingTemplate, enforcedClass) \
  friend void enforcingCtors(){static enforcingTemplate<enforcedClass> _enforcer;}
#endif


template<class T>
      class AbstractEnforcer : T{
        public:
          explicit AbstractEnforcer(){
            T enforcedCtor0(                                     );
            T enforcedCtor1( *(new unsigned int)                 );
            T enforcedCtor2( *(new unsigned int), *(new QString) );
            T enforcedCtor3( *(new unsigned int), *(new float  ) );
          }
      };
#endif

and in each class that I wan't to enforce I just add like this:



class X{
  ENFORCE_CTORS(AbstractEnforcer, X);
  /*
    .....
  */
}

I didn't find any other way to inject this code dynamically in a class. And I may been unclear about the final purpose of the operation (sorry for my terrible English).

chedi
I'm just keeping the *(new typename) parameters style notation to explicit the argument type, because in reality the code execution is only done in the debug phase of the project and the code is simply ignored in the release cycle.
chedi