tags:

views:

2667

answers:

14

Anyone knows if is possible to have partial class definition on C++ ?

Something like:

file1.h:

  
class Test {
    public:
        int test1();
};

file2.h:

class Test {
    public:
        int test2();
};

For me it seems quite useful for definining multi-platform classes that have common functions between them that are platform-independent because inheritance is a cost to pay that is non-useful for multi-platform classes.

I mean you will never have two multi-platform specialization instances at runtime, only at compile time. Inheritance could be useful to fulfill your public interface needs but after that it won't add anything useful at runtime, just costs.

Also you will have to use an ugly #ifdef to use the class because you can't make an instance from an abstract class:

class genericTest {
    public:
        int genericMethod();
};

Then let's say for win32:

class win32Test: public genericTest {
    public:
        int win32Method();
};

And maybe:

class macTest: public genericTest {
    public:
        int macMethod();
};

Let's think that both win32Method() and macMethod() calls genericMethod(), and you will have to use the class like this:

 #ifdef _WIN32
 genericTest *test = new win32Test();
 #elif MAC
 genericTest *test = new macTest();
 #endif

 test->genericMethod();

Now thinking a while the inheritance was only useful for giving them both a genericMethod() that is dependent on the platform-specific one, but you have the cost of calling two constructors because of that. Also you have ugly #ifdef scattered around the code.

That's why I was looking for partial classes. I could at compile-time define the specific platform dependent partial end, of course that on this silly example I still need an ugly #ifdef inside genericMethod() but there is another ways to avoid that.

+11  A: 

Try inheritance

Specifically

class AllPlatforms {
public:
    int common();
};

and then

class PlatformA : public AllPlatforms {
public:
    int specific();
};
Jamie
+11  A: 

This is not possible in C++, it will give you an error about redefining already-defined classes. If you'd like to share behavior, consider inheritance.

John Millikin
A: 

As written, it is not possible.

You may want to look into namespaces. You can add a function to a namespace in another file. The problem with a class is that each .cpp needs to see the full layout of the class.

Andrew Stein
A: 

Nope.

But, you may want to look up a technique called "Policy Classes". Basically, you make micro-classes (that aren't useful on their own) then glue them together at some later point.

eduffy
A: 

Declaring a class body twice will likely generate a type redefinition error. If you're looking for a work around. I'd suggest #ifdef'ing, or using an Abstract Base Class to hide platform specific details.

luke
+2  A: 

Either use inheritance, as Jamie said, or #ifdef to make different parts compile on different platforms.

Lev
+1  A: 

Since headers are just textually inserted, one of them could omit the "class Test {" and "}" and be #included in the middle of the other.

I've actually seen this in production code, albeit Delphi not C++. It particularly annoyed me because it broke the IDE's code navigation features.

Hugh Allen
This is very misguided, very misguided, but a valid solution I think :-)
kervin
+2  A: 

For me it seems quite useful for definining multi-platform classes that have common functions between them that are platform-independent.

Except developers have been doing this for decades without this 'feature'.

I believe partial was created because Microsoft has had, for decades also, a bad habit of generating code and handing it off to developers to develop and maintain.

Generated code is often a maintenance nightmare. What habits to that entire MFC generated framework when you need to bump your MFC version? Or how do you port all that code in *.designer.cs files when you upgrade Visual Studio?

Most other platforms rely more heavily on generating configuration files instead that the user/developer can modify. Those, having a more limited vocabulary and not prone to be mixed with unrelated code. The configuration files can even be inserted in the binary as a resource file if deemed necessary.

I have never seen 'partial' used in a place where inheritance or a configuration resource file wouldn't have done a better job.

kervin
A: 

You can get something like partial classes using template specialization and partial specialization. Before you invest too much time, check your compiler's support for these. Older compilers like MSC++ 6.0 didn't support partial specialization.

+5  A: 

or you could try PIMPL

common header file:

class Test
{
public:
    ...
    void common();
    ...
private:
    class TestImpl;
    TestImpl* m_customImpl;
};

Then create the cpp files doing the custom implementations that are platform specific.

PiNoYBoY82
You need a pointer to TestImpl, not an instance!
coppro
whoops thanks for the catch
PiNoYBoY82
FTW, PIMPL stands for "pointer to implementation" idiom.
Steve Fallows
+2  A: 

How about this:

class WindowsFuncs { public: int f(); int winf(); };
class MacFuncs { public: int f(); int macf(); }

class Funcs
#ifdef Windows 
    : public WindowsFuncs
#else
    : public MacFuncs
#endif
{
public:
    Funcs();
    int g();
};

Now Funcs is a class known at compile-time, so no overheads are caused by abstract base classes or whatever.

pdc
+1  A: 
#include will work as that is preprocessor stuff.

class Foo
{
#include "FooFile_Private.h"
}

////////

FooFile_Private.h:

private:
  void DoSg();
+3  A: 

You can't partially define classes in C++.

Here's a way to get the "polymorphism, where there's only one subclass" effect you're after without overhead and with a bare minimum of #define or code duplication. It's called simulated dynamic binding:

template <typename T>
class genericTest {
public:
    void genericMethod() {
        // do some generic things
        std::cout << "Could be any platform, I dunno" << std::endl;
        // base class can call a method in the child with static_cast
        (static_cast<T*>(this))->doClassDependentThing();
    }
};

#ifdef _WIN32
    typedef Win32Test Test;
#elif MAC
    typedef MacTest Test;
#endif

Then off in some other headers you'll have:

class Win32Test : public genericTest<Win32Test> {
public:
    void win32Method() {
        // windows-specific stuff:
        std::cout << "I'm in windows" << std::endl;
        // we can call a method in the base class
        genericMethod();
        // more windows-specific stuff...
    }
    void doClassDependentThing() {
        std::cout << "Yep, definitely in windows" << std::endl;
    }
};

and

class MacTest : public genericTest<MacTest> {
public:
    void macMethod() {
        // mac-specific stuff:
        std::cout << "I'm in MacOS" << std::endl;
        // we can call a method in the base class
        genericMethod();
        // more mac-specific stuff...
    }
    void doClassDependentThing() {
        std::cout << "Yep, definitely in MacOS" << std::endl;
    }
};

This gives you proper polymorphism at compile time. genericTest can non-virtually call doClassDependentThing in a way that gives it the platform version, (almost like a virtual method), and when win32Method calls genericMethod it of course gets the base class version.

This creates no overhead associated with virtual calls - you get the same performance as if you'd typed out two big classes with no shared code. It may create a non-virtual call overhead at con(de)struction, but if the con(de)structor for genericTest is inlined you should be fine, and that overhead is in any case no worse than having a genericInit method that's called by both platforms.

Client code just creates instances of Test, and can call methods on them which are either in genericTest or in the correct version for the platform. To help with type safety in code which doesn't care about the platform and doesn't want to accidentally make use of platform-specific calls, you could additionally do:

#ifdef _WIN32
    typedef genericTest<Win32Test> BaseTest;
#elif MAC
    typedef genericTest<MacTest> BaseTest;
#endif

You have to be a bit careful using BaseTest, but not much more so than is always the case with base classes in C++. For instance, don't slice it with an ill-judged pass-by-value. And don't instantiate it directly, because if you do and call a method that ends up attempting a "fake virtual" call, you're in trouble. The latter can be enforced by ensuring that all of genericTest's constructors are protected.

Steve Jessop
I think you got the same line of thought, I liked it.
Augusto Radtke
You should be able to find more tips and tricks by searching on "simulated dynamic binding" or maybe "static polymorphism". I'm not sure I've ever seriously used it, since in practice it's quite rare for the overhead of virtual calls to actually matter. If it ever does, though, I'll be ready :-)
Steve Jessop
A: 

Partial classes are good if you want to extend classes without touching original files. For example I want to extend std's vector template class with helpers. Why the hell I have to create inherited class like vectorEx and not just add methods through partial ?

If you could extend it directly, you could access vector's private members which makes your code dependent on its implementation. A more technical reason is that the class could be compiled separately and would break if you changed its structure.
Matthew Crumley