views:

282

answers:

4

Below code instantiates a derived singleton object based on environment variable. The compiler errors saying error C2512: 'Dotted' : no appropriate default constructor. I don't understand what the compiler is complaining about.

EDIT: Fixed issues with implementing the get instance method which requires definition of both the parent and derived class. By separating the class definitions in separate header files and including the same in Singleton.cpp where the instance function is implemented.

Mainfile – 1
#include <iostream>
#include <string>
#include "Singleton.h"
using namespace std;

int main(){

     Singleton::instant().print();
     cin.get();
}
Singleton.h
#pragma once
#include <iostream>
using std::cout;
class Singleton{
public:
    static Singleton & instant();
    virtual void print(){cout<<"Singleton";}
protected:
    Singleton(){};
private:
    static Singleton * instance_;
    Singleton(const Singleton & );
    void operator=(const Singleton & );

};

Singleton.cpp
#include "Singleton.h"
#include "Dotted.h"
Singleton * Singleton::instance_ = 0;
Singleton & Singleton::instant(){
    if (!instance_)
    {
        char * style = getenv("STYLE");
        if (style){
            if (strcmp(style,"dotted")==0)
            {
                instance_ = new Dotted();
                return *instance_; 
            }           else{
                instance_ = new Singleton();
                return *instance_;
            }       
        }

        else{
            instance_ = new Singleton();
            return *instance_;
        }
    }
    return *instance_;

}
Dotted.h

#pragma once
class Dotted;

class Dotted:public Singleton{
public:
    friend class Singleton;
    void print(){cout<<"Dotted";}
    private:
        Dotted(){};

};
+1  A: 
Michael Aaron Safyan
I should point out that "avoid singletons" is a matter of opinion. I've used them throughout my programming career with no problems whatsoever.
anon
@Neil, I agree that it is an opinion, but singletons and global variables frequently lead to absolutely contorted code. A better approach, IMHO, is to pass in an interface and simply have it so that you happen to construct only one object that implements it.
Michael Aaron Safyan
@Michael Aaron Safyan I couldn't take your opinion because singletons are pretty useful provided you know the how not to use it. And its dark corners.There is a chapter dedicated on singleton in Modern C++ and in More Effective C++.
yesraaj
@Michael Aaron Safyan oop thanks. Still the code compiles with error.
yesraaj
There is a chapter dedicated in Modern C++ and More Effective C++ because Singletons are often used and are hard to get right: it does not mean they should be used. I use them too, out of convenience, but global variables hurt testability and run against Dependency Injection. In an ideal world, we could perhaps only use Dependency Injection, but it may contort the design at which point it seems pretty useless, furthermore it means exposing implementations details too :x Anyway I have personally begin to replace my Singletons by Monoid, which has the advantage of hiding the shared state.
Matthieu M.
@yesraaj, is it giving the same error?
Michael Aaron Safyan
@Michael Aaron Safyan yes :(
yesraaj
@yesraaj, see my other corrections.
Michael Aaron Safyan
@Michael Aaron Safyan I think it should work now instance class needs the definition of both the classes so I need to define those before hand :)
yesraaj
@Michael Aaron Safyan The above code is just a throw away code just tried out an alternative implementation described in GOF book, so didn't care to separate the classes and having a unique name for environment variable and all. I was trying out different ways of implementing the singleton this afternoon, didn't notice typical error. Thanks you helped.
yesraaj
@Michael Aaron Safyan I still don't understand the purpose of Printer and StringPrinter , I would appreciate you explanation :).
yesraaj
@yesraaj, the purpose of Printer and StringPrinter is to provide an alternative to your singleton; all your singleton does is print... so why not simply create an interface for that behavior and pass around that interface? The StringPrinter is a possible implementation of that interface. You can achieve the "singleton" property merely by constructing only one instance that you pass around, which is a more flexible approach to singleton-ness than to bake the fact that it is singleton into the static singleton construction method. See my article for more info about dependency injection.
Michael Aaron Safyan
@yesraaj, also, you should not rely on "#pragma once" and instead use preprocessor guards as "#pragma" is compiler-specific, and most compilers will optimize preprocessor guards, anyway.
Michael Aaron Safyan
@Michael Aaron Safyan ok, I will look into those two videos.
yesraaj
A: 

I don't see how this can work. At this point:

instance_ = new Dotted();

Dotted is an incomplete type. With g++ 4.4.1 I get:

error: invalid use of incomplete type 'struct Dotted'

Which compiler are you using?

anon
@Neil Butterworth Microsoft Visual C++ 2005
yesraaj
@yesraj I don't think you are posting the same code as you are compiling.
anon
@Neil Butterworth no I am pasting the same code. I think @Michael Aaron Safyan answer should work. the problem is with not separating the classes in separate translation unit.
yesraaj
A: 

The first error I'm getting is: strcmp not declared. Hint: it's in the <cstring> (≈ <string.h>) header.

After that the next error is:

instance_ = new Dotted();

"Invalid use of incomplete type."

If you use forward declarations, you must separate the declarations and implementations, so you can do things that require complete types after they are defined.

UncleBens
+1  A: 

It is not a great error message. The problem is that the compiler cannot generate code for the constructor call, it hasn't yet seen the definition of the Dotted class. C++ compilers are still single-pass compilers. You cannot write the method inline, you have to move it.

class Singleton {
public:
    static Singleton & instant();
    // etc..
};

class Dotted : public Singleton {
    // etc..
};

// Now it works:
Singleton & Singleton::instant() {
    // etc..
}
Hans Passant