Related
I would like to split a class implementation into three parts, to avoid that users need to deal with the implementation details, e.g., the libaries that I use to implement the functionality:
impl.cpp
#include <api.h>
#include <impl.h>
Class::Class() {
init();
}
Class::init() {
myData = SomeLibrary::Type(42);
}
Class::doSomething() {
myData.doSomething();
}
impl.h
#include <somelibrary.h>
class Class {
public:
Class();
init();
doSomething();
private:
SomeLibary::Type myData;
}
api.h
class Class {
Class();
doSomething();
}
The problem is, that I am not allowed to redefine headers for the class definition. This does not work when I define Class() and doSomething() only in api.h, either.
A possible option is to define api.h and do not use it in the project at all, but install it (and do not install impl.h).
The obvious drawback is, that I need to make sure, that the common methods in api.h and impl.h always have the same signature, otherwise programs using the library will get linker errors, that I cannot predict when compiling the library.
But would this approach work at all, or will I get other problems (e.g. wrong pointers to class members or similar issues), because the obj file does not match the header?
The short answer is "No!"
The reason: any/all 'client' projects that need to use your Class class have to have the full declaration of that class, in order that the compiler can properly determine such things as offsets for member variables.
The use of private members is fine - client programs won't be able to change them - as is your current implementation, where only the briefest outlines of member functions are provided in the header, with all actual definitions in your (private) source file.
A possible way around this is to declare a pointer to a nested class in Class, where this nested class is simply declared in the shared header: class NestedClass and then you can do what you like with that nested class pointer in your implementation. You would generally make the nested class pointer a private member; also, as its definition is not given in the shared header, any attempt by a 'client' project to access that class (other than as a pointer) will be a compiler error.
Here's a possible code breakdown (maybe not error-free, yet, as it's a quick type-up):
// impl.h
struct MyInternal; // An 'opaque' structure - the definition is For Your Eyes Only
class Class {
public:
Class();
init();
doSomething();
private:
MyInternal* hidden; // CLient never needs to access this! Compiler error if attempted.
}
// impl.cpp
#include <api.h>
#include <impl.h>
struct MyInternal {
SomeLibrary::Type myData;
};
Class::Class() {
init();
}
Class::init() {
hidden = new MyInternal; // MUCH BETTER TO USE unique_ptr, or some other STL.
hidden->myData = SomeLibrary::Type(42);
}
Class::doSomething() {
hidden->myData.doSomething();
}
NOTE: As I hinted in a code comment, it would be better code to use std::unique_ptr<MyInternal> hidden. However, this would require you to give explicit definitions in your Class for the destructor, assignment operator and others (move operator? copy constructor?), as these will need access to the full definition of the MyInternal struct.
The private implementation (PIMPL) idiom can help you out here. It will probably result in 2 header and 2 source files instead of 2 and 1. Have a silly example I haven't actually tried to compile:
api.h
#pragma once
#include <memory>
struct foo_impl;
struct foo {
int do_something(int argument);
private:
std::unique_ptr<foo_impl> impl;
}
api.c
#include "api.h"
#include "impl.h"
int foo::do_something(int a) { return impl->do_something(); }
impl.h
#pragma once
#include <iostream>
struct foo_impl {
foo_impl();
~foo_impl();
int do_something(int);
int initialize_b();
private:
int b;
};
impl.c
#include <iostream>
foo_impl::foo_impl() : b(initialize_b()} { }
foo_impl::~foo_impl() = default;
int foo_impl::do_something(int a) { return a+b++; }
int foo_impl::initialize_b() { ... }
foo_impl can have whatever methods it needs, as foo's header (the API) is all the user will see. All the compiler needs to compile foo is the knowledge that there is a pointer as a data member so it can size foo correctly.
I recently got this idea to separate different platform specific implementations (could be Win32/X, opengl/dx/vulkan, etc...) using CRTP (curiously recurring template pattern): I thought of something like this:
IDisplayDevice.h
#pragma once
#include "OSConfig.h"
namespace cbn
{
template <class TDerived> // Win32 type here
struct IDisplayDevice
{
bool run_frame(void) {
return
static_cast<const TDerived*>(this)->run_frame();
}
// a lot of other methods ...
};
}
Win32DisplayDevice.h:
#pragma once
#include "OSConfig.h"
// make sure it only gets compiled on win32/64
#if defined(CBN_OS_WINDOWS)
namespace cbn
{
class CWin32DisplayDevice
: public IDisplayDevice<CWin32DisplayDevice> {
public:
bool run_frame(void) {
call_hInstance();
call_hWnd();
#ifdef CBN_RENDERAPI_DX11
call_dx11_bufferswap();
#endif
return some_state;
}
private:
};
}
#endif
I would then provide an other implementation the same way in XDisplayDevice.h.
Finally, I would make a common interface in DisplayDevice.h:
#include "Win32DisplayDevice.h"
#include "XDisplayDevice.h"
namespace cbn
{
class CDisplayDevice
{
public:
CBN_INLINE
bool run_frame(void) { return device_->run_frame(); }
private:
#if defined(CBN_OS_WINDOWS)
CWin32DisplayDevice device_;
#elif defined(CBN_OS_LINUX)
CXDisplayDevice device_;
#elif // and so on
#else
// does nothing ...
CNillDisplayDevice device_;
#endif
}
}
So I could call it in main.cpp like:
int main()
{
CDisplayDevice my_device;
while(my_device->run_frame())
{
do_some_magic();
}
}
Do you think this would be a good way to deal with platform specific code ?
PS: I avoid victuals and polymorphism because of platform restraints (android, ps4, etc...) where pointer calls matter.
Consider this code:
struct OpenGLTraits // keep this in it's own files (.h and .cpp)
{
bool run_frame() { /* open gl specific stuff here */ }
};
struct VulkanTraits // keep this in it's own files (.h and .cpp)
{
bool run_frame() { /* vulkan specific stuff here */ }
};
template<typename T>
class DisplayDevice
{
using graphic_traits = T;
graphic_traits graphics; // maybe inject this in constructor?
void do_your_operation()
{
if(!graphics.run_frame()) // subsystem-specific call
{ ... }
}
};
This will use subsystem-specific calls, and abstract them away between a common API, without a virtual call involved. You can even inline the run_frame() implementations.
Edit (address comment question):
consider this:
#ifdef FLAG_SPECIFYING_OPEN_GL
using Device = DisplayDevice<OpenGLTraits>;
#elif FLAG_SPECIFYING_VULKAN
using Device = DisplayDevice<VulkanTraits>;
...
#endif
client code:
Device device;
device.do_your_operation();
I don't really see the benefit of CRTP here, you still have platform specific (as opposed to feature specific) ifdefs within the code, and this tends to make things harder to read and maintain. I usually prefer having different implementations in different source files - and in fact, generally having seperate directories for each platform.
such as:
platform/win64
platform/win32
platform/gnu-linux
platform/freebsd
In this way you can largely avoid the ifdef clutter, and you generally know where to find the platform specific things. You also know what you need to write in order to port things to another platform. The build system can then be made to select the correct source rather than the preprocessor.
In my quest to create a cross-platform GUI Framework, I have hit the following snag:
Suppose I have a central "Window" class, in the project's general, platform-independent include folder:
//include/window.hpp
class Window
{
//Public interface
}
I then have several platform-dependent implementation classes, like so:
//src/{platform}/window.hpp
class WinWindow {...}; //Windows
class OSXWindow {...}; //OSX
class X11Window {...}; //Unix
Finally, there is the original Window class' .cpp file, where I want to "bind" the implementation class to the general class. Purely conceptually, this is what I want to be able to do:
//src/window.cpp
//Suppose we're on Windows
#include "include/window.hpp"
#include "src/win/window.hpp"
class Window : private WinWindow; //Redefine Window's inheritance
I know this is by no means valid C++, and that's the point. I have thought of two possible ways to solve this problem, and I have problems with both.
pImpl-style implementation
Make Window hold a void pointer to an implementing class, and assign that to a different window class for each platform. However, I would have to up-cast the pointer every time I want to perform a platform dependent-operation, not to mention include the platform dependent file everywhere.
Preprocessor directives
class Window :
#ifdef WIN32
private WinWindow
#else ifdef X11
private X11Window //etc.
This, however, sounds more like a hack than an actual solution to the problem.
What to do? Should I change my design completely? Do any of my possible solutions hold a little bit of water?
Using typedef to hide the preprocessor
You could simply typedef the appropriate window type instead:
#ifdef WINDOWS
typedef WinWindow WindowType;
#elif defined // etc
Then your window class could be:
class Window : private WindowType {
};
This isn't a very robust solution, though. It is better to think in a more Object Oriented way, but OO programming in C++ comes at a runtime cost, unless you use the
Curiously repeating template pattern
You could use the curiously repeating template pattern:
template<class WindowType>
class WindowBase {
public:
void doSomething() {
static_cast<WindowType *>(this)->doSomethingElse();
}
};
Then you could do
class WinWindow : public WindowBase<WinWindow> {
public:
void doSomethingElse() {
// code
}
};
And to use it (assuming C++ 14 support):
auto createWindow() {
#ifdef WINDOWS
return WinWindow{};
#elif UNIX
return X11Window{};
#endif
}
With C++ 11 only:
auto createWindow()
->
#ifdef WINDOWS
WinWindow
#elif defined UNIX
X11Window
#endif
{
#ifdef WINDOWS
return WinWindow{};
#elif defined UNIX
return X11Window{};
#endif
}
I recommend using auto when you use it, or using it in combination with a typedef:
auto window = createWindow();
window.doSomething();
Object Oriented Style
You could make your Window class be an abstract class:
class Window {
protected:
void doSomething();
public:
virtual void doSomethingElse() = 0;
};
Then define your platform-dependent classes as subclasses of Window. Then all you'd have to do is have the preprocessor directives in one place:
std::unique_ptr<Window> createWindow() {
#ifdef WINDOWS
return new WinWindow;
#elif defined OSX
return new OSXWindow;
// etc
}
Unfortunately, this incurs a runtime cost through calls to the virtual function. The CRTP version resolves calls to the "virtual function" at compile time instead of at runtime.
Additionally, this requires the Window to be declared on the heap whereas CRTP doesn't; this might be a problem depending on the use case, but in general, it doesn't matter that much.
Ultimately, you do have to use the #ifdef somewhere, so you can determine the platform (or you could use a library that determines the platform, but it probably uses #ifdef too), the question is just where to hide it.
You can use the CRTP pattern to implement static polymorphism:
class WindowBase {
virtual void doSomething() = 0;
};
template<class WindowType>
class Window : public WindowBase {
// Static cast when accessing the actual implementation:
void doSomething() {
static_cast<WindowType*>(this)->doSomethingElse();
}
};
class X11WindowImpl : public Window<X11WindowImpl> {
void doSomethingElse() {
// blah ...
}
};
class Win32WindowImpl : public Window<Win32WindowImpl> {
void doSomethingElse() {
// blah ...
}
};
Since your code will be compiled to satisfy a particular target, this should be the leanest option.
It's okey. You could also write one class and define it's content using #ifdef etc., but your solution isn't a hack. Just a proper way to write multiplatform code if you have no other choice.
I'm trying to find a clean way to separate implementation details in C++ header files in a big project in order to achieve better information hiding and reduce build time. The problem with C++ is that every time you change a private member declaration, your dependent classes must be rebuilt.
This is a solution I came up with. Is it any good?
The basic Idea is to include a part of the cpp file conditionally in the header. this part contains the implementation declarations and is included only when the implementation file includes the header. in case of the external classes, this details are excluded from header. so client and implementation see two different version of header file. internal declaration changes won't affect clients(no compilation of dependent classes) and headers won't include private details.
Here is the implementation:
HEADER
#pragma once
class Dependency
{
public:
Dependency(void);
~Dependency(void);
void Proc(void);
//PRIVATE Implementaion details stays private
#ifdef Dependency_PRIVATE_IMPELEMENTATION
#define Dependency_PRIVATE_MODE 1
#include "Dependency.cpp"
#undef Dependency_PRIVATE_MODE
#endif
};
CPP
#define Dependency_PRIVATE_IMPELEMENTATION
#include "Dependency.h"
#undef Dependency_PRIVATE_IMPELEMENTATION
#ifdef Dependency_PRIVATE_MODE
private:
int _privateData;
#else
#include <iostream>
Dependency::Dependency(void)
{
//This line causes a runtime exception, see client
Dependency::_privateData = 0;
}
Dependency::~Dependency(void)
{
}
void Dependency::Proc(void)
{
std::cout << "Shiny happy functions.";
}
#endif
CLIENT
#include "stdafx.h"
#include "Dependency.h"
#pragma message("Test.Cpp Compiled")
int _tmain(int argc, _TCHAR* argv[])
{
Dependency d;
d.Proc();
return 0;
//and how I have a run time check error #2, stack around d ?!!
}
It's a pretty interesting question, really. Managing dependencies is important for big projects because the build times ramp up can make even the simplest change daunting... and when it happens people will try to hack it to avoid the rebuild-of-death (tm).
Unfortunately, it does not work.
The Standard explicitly says that classes definitions appearing in different translation units (roughly, files) should obey the One Definition Rule (see ยง 3.2 One definition rule [basic.def.odr]).
Why ?
The problem is a matter of impedance, in a way. The definition of a class contains information on the class ABI (Application Binary Interface), most notably, how such a class is layed out in memory. If you have different layouts of the same class in various translation units, then when putting it altogether, it won't work. It's as if one TU was speaking German and the other Korean. They might be attempting to say the same thing, they just won't understand each other.
So ?
There are several ways to manage dependencies. The main idea is that you should struggle, as much as possible, to provide "light" headers:
include as few things as possible. You can forward declare: types that are shown as arguments or return of functions declaration, types that are passed by reference or pointer but otherwise unused.
hide implementation details
Hum... What does it mean :x ?
Let's pick a simple example, shall we ?
#include "project/a.hpp" // defines class A
#include "project/b.hpp" // defines class B
#include "project/c.hpp" // defines class C
#include "project/d.hpp" // defines class D
#include "project/e.hpp" // defines class E
namespace project {
class MyClass {
public:
explicit MyClass(D const& d): _a(d.a()), _b(d.b()), _c(d.c()) {}
MyClass(A a, B& b, C* c): _a(a), _b(b), _c(c) {}
E e() const;
private:
A _a;
B& _b;
C* _c;
}; // class MyClass
} // namespace project
This header pulls in 5 other headers, but how many are actually necessary ?
a.hpp is necessary, because _a of type A is an attribute of the class
b.hpp is not necessary, we only have a reference to B
c.hpp is not necessary, we only have a pointer to C
d.hpp is necessary, we call methods on D
e.hpp is not necessary, it only appears as a return
OK, let's clean this up!
#include "project/a.hpp" // defines class A
#include "project/d.hpp" // defines class D
namespace project { class B; }
namespace project { class C; }
namespace project { class E; }
namespace project {
class MyClass {
public:
explicit MyClass(D const& d): _a(d.a()), _b(d.b()), _c(d.c()) {}
MyClass(A a, B& b, C* c): _a(a), _b(b), _c(c) {}
E e() const;
private:
A _a;
B& _b;
C* _c;
}; // class MyClass
} // namespace project
Can we do better ?
Well, first we can see that we call methods on D only in the constructor of the class, if we move the definition of D out of the header, and put it in a .cpp file, then we won't need to include d.hpp any longer!
// no need to illustrate right now ;)
But... what of A ?
It is possible to "cheat", by remarking that merely holding a pointer does not requires a full definition. This is known as the Pointer To Implementation idiom (pimpl for short). It trades off run time for lighter dependencies, and adds some complexity to the class. Here is a demo:
#include <memory> // don't really worry about std headers,
// they are pulled in at one time or another anyway
namespace project { class A; }
namespace project { class B; }
namespace project { class C; }
namespace project { class D; }
namespace project { class E; }
namespace project {
class MyClass {
public:
explicit MyClass(D const& d);
MyClass(A a, B& b, C* c);
~MyClass(); // required to be in the source file now
// because for deleting Impl,
// the std::unique_ptr needs its definition
E e() const;
private:
struct Impl;
std::unique_ptr<Impl> _impl;
}; // class MyClass
} // namespace project
And the corresponding source file, since that were the interesting things occur:
#include "project/myClass.hpp" // good practice to have the header included first
// as it asserts the header is free-standing
#include "project/a.hpp"
#include "project/b.hpp"
#include "project/c.hpp"
#include "project/d.hpp"
#include "project/e.hpp"
struct MyClass::Impl {
Impl(A a, B& b, C* c): _a(a), _b(b), _c(c) {}
A _a;
B& _b;
C* _c;
};
MyClass::MyClass(D const& d): _impl(new Impl(d.a(), d.b(), d.c())) {}
MyClass::MyClass(A a, B& b, C* c): _impl(new Impl(a, b, c)) {}
MyClass::~MyClass() {} // nothing to do here, it'll be automatic
E MyClass::e() { /* ... */ }
Okay, so that was the low and gritty. Further reading:
The Law of Demeter: avoid having to call multiple methods in sequences (a.b().c().d()), it means you have leaky abstraction, and forces you the include the whole world to do anything. Instead, you should be calling a.bcd() which hides the details from you.
Separate your code into modules, and provide a clear-well defined interface to each module, normally, you should have far more code within the module than on its surface (ie exposed headers).
There are many ways to encapsulate and hide information, your quest just begins!
This does not work. If you add anything to the class in the private .cpp file, the the users of the class will see a different class than what your implementation thinks it is.
This is not legal, and won't work in many cases. KDE has a great article on what you can and can't change in C++ to preserve ABI compatibility: Binary Compatibility Issues. If you break any of that with your "hidden" implementation, you're going to break the users.
Look at the pimpl idiom for a rather common way of doing what you are trying to achieve.
This won't work. You can easily see it because sizeof(Dependency) for the implementation and the client are different. The client basically sees a different class, accesses different locations in the memory and everything messes up!
Unfortunately, you cannot prevent a rebuild of dependent files if you change a class. However, you can hide the implementation details like this:
Header:
class privateData;
class Dependency
{
private:
privateData *pd;
public:
Dependency(void);
~Dependency(void);
void Proc(void);
};
cpp file
#include <Dependency.h>
class privateData
{
/* your data here */
};
Dependency::Dependency()
{
pd = new privateData;
}
Dependency::~Dependency()
{
if (pd)
delete pd;
}
void Dependency::Proc()
{
/* your code */
}
Note that this is not for you to copy paste. It's just to give you the idea. There may be missing error checking or code that is implied by this usage. One such thing is a copy constructor to prevent shallow copies.
Look at the Opaque_pointer pattern (aka pImpl)
The pattern is typically used for when the Class want to hide the internal implementation but is also have the benefit of that changes to the internal and private structures does not create a recompile since the binary call compatibility is maintained.
The problem with doing it any other way, is that binary compatibility is probably NOT maintained when you changed anything in the class definition, and hence all software will have to be recompiled.
It looks like your solution is a attempt to do exactly this, however you should user a (void*) instead of a int so as to make sure the software compiles correctly on 32 and 64 bit compilers on different platforms -- and just use the cook-book example of Opaque Pointers.
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.
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.
Try inheritance
Specifically
class AllPlatforms {
public:
int common();
};
and then
class PlatformA : public AllPlatforms {
public:
int specific();
};
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 don't know" << 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.
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.
#include will work as that is preprocessor stuff.
class Foo
{
#include "FooFile_Private.h"
}
////////
FooFile_Private.h:
private:
void DoSg();
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.
As written, it is not possible, and in some cases it is actually annoying.
There was an official proposal to the ISO, with in mind embedded software, in particular to avoid the RAM ovehead given by both inheritance and pimpl pattern (both approaches require an additional pointer for each object):
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0309r0.pdf
Unfortunately the proposal was rejected.
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.
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.
Either use inheritance, as Jamie said, or #ifdef to make different parts compile on different platforms.
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.
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.
Dirty but practical way is using #include preprocessor:
Test.h:
#ifndef TEST_H
#define TEST_H
class Test
{
public:
Test(void);
virtual ~Test(void);
#include "Test_Partial_Win32.h"
#include "Test_Partial_OSX.h"
};
#endif // !TEST_H
Test_Partial_OSX.h:
// This file should be included in Test.h only.
#ifdef MAC
public:
int macMethod();
#endif // MAC
Test_Partial_WIN32.h:
// This file should be included in Test.h only.
#ifdef _WIN32
public:
int win32Method();
#endif // _WIN32
Test.cpp:
// Implement common member function of class Test in this file.
#include "stdafx.h"
#include "Test.h"
Test::Test(void)
{
}
Test::~Test(void)
{
}
Test_Partial_OSX.cpp:
// Implement OSX platform specific function of class Test in this file.
#include "stdafx.h"
#include "Test.h"
#ifdef MAC
int Test::macMethod()
{
return 0;
}
#endif // MAC
Test_Partial_WIN32.cpp:
// Implement WIN32 platform specific function of class Test in this file.
#include "stdafx.h"
#include "Test.h"
#ifdef _WIN32
int Test::win32Method()
{
return 0;
}
#endif // _WIN32
Suppose that I have:
MyClass_Part1.hpp, MyClass_Part2.hpp and MyClass_Part3.hpp
Theoretically someone can develop a GUI tool that reads all these hpp files above and creates the following hpp file:
MyClass.hpp
class MyClass
{
#include <MyClass_Part1.hpp>
#include <MyClass_Part2.hpp>
#include <MyClass_Part3.hpp>
};
The user can theoretically tell the GUI tool where is each input hpp file and where to create the output hpp file.
Of course that the developer can theoretically program the GUI tool to work with any varying number of hpp files (not necessarily 3 only) whose prefix can be any arbitrary string (not necessarily "MyClass" only).
Just don't forget to #include <MyClass.hpp> to use the class "MyClass" in your projects.
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.
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.
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.
I do agree on this. Partial classes is strange construct that makes it very difficult to maintain afterwards. It is difficult to locate on which partial class each member is declared and redefinition or even reimplementation of features are hard to avoid.
Do you want to extend the std::vector, you have to inherit from it. This is because of several reasons. First of all you change the responsibility of the class and (properly?) its class invariants. Secondly, from a security point of view this should be avoided.
Consider a class that handles user authentication...
partial class UserAuthentication {
private string user;
private string password;
public bool signon(string usr, string pwd);
}
partial class UserAuthentication {
private string getPassword() { return password; }
}
A lot of other reasons could be mentioned...
Let platform independent and platform dependent classes/functions be each-others friend classes/functions. :)
And their separate name identifiers permit finer control over instantiation, so coupling is looser. Partial breaks encapsulation foundation of OO far too absolutely, whereas the requisite friend declarations barely relax it just enough to facilitate multi-paradigm Separation of Concerns like Platform Specific aspects from Domain-Specific platform independent ones.
I've been doing something similar in my rendering engine. I have a templated IResource interface class from which a variety of resources inherit (stripped down for brevity):
template <typename TResource, typename TParams, typename TKey>
class IResource
{
public:
virtual TKey GetKey() const = 0;
protected:
static shared_ptr<TResource> Create(const TParams& params)
{
return ResourceManager::GetInstance().Load(params);
}
virtual Status Initialize(const TParams& params, const TKey key, shared_ptr<Viewer> pViewer) = 0;
};
The Create static function calls back to a templated ResourceManager class that is responsible for loading, unloading, and storing instances of the type of resource it manages with unique keys, ensuring duplicate calls are simply retrieved from the store, rather than reloaded as separate resources.
template <typename TResource, typename TParams, typename TKey>
class TResourceManager
{
sptr<TResource> Load(const TParams& params) { ... }
};
Concrete resource classes inherit from IResource utilizing the CRTP. ResourceManagers specialized to each resource type are declared as friends to those classes, so that the ResourceManager's Load function can call the concrete resource's Initialize function. One such resource is a texture class, which further uses a pImpl idiom to hide its privates:
class Texture2D : public IResource<Texture2D , Params::Texture2D , Key::Texture2D >
{
typedef TResourceManager<Texture2D , Params::Texture2D , Key::Texture2D > ResourceManager;
friend class ResourceManager;
public:
virtual Key::Texture2D GetKey() const override final;
void GetWidth() const;
private:
virtual Status Initialize(const Params::Texture2D & params, const Key::Texture2D key, shared_ptr<Texture2D > pTexture) override final;
struct Impl;
unique_ptr<Impl> m;
};
Much of the implementation of our texture class is platform-independent (such as the GetWidth function if it just returns an int stored in the Impl). However, depending on what graphics API we're targeting (e.g. Direct3D11 vs. OpenGL 4.3), some of the implementation details may differ. One solution could be to inherit from IResource an intermediary Texture2D class that defines the extended public interface for all textures, and then inherit a D3DTexture2D and OGLTexture2D class from that. The first problem with this solution is that it requires users of your API to be constantly mindful of which graphics API they're targeting (they could call Create on both child classes). This could be resolved by restricting the Create to the intermediary Texture2D class, which uses maybe a #ifdef switch to create either a D3D or an OGL child object. But then there is still the second problem with this solution, which is that the platform-independent code would be duplicated across both children, causing extra maintenance efforts. You could attempt to solve this problem by moving the platform-independent code into the intermediary class, but what happens if some of the member data is used by both platform-specific and platform-independent code? The D3D/OGL children won't be able to access those data members in the intermediary's Impl, so you'd have to move them out of the Impl and into the header, along with any dependencies they carry, exposing anyone who includes your header to all that crap they don't need to know about.
API's should be easy to use right and hard to use wrong. Part of being easy to use right is restricting the user's exposure to only the parts of the API they should be using. This solution opens it up to be easily used wrong and adds maintenance overhead. Users should only have to care about the graphics API they're targeting in one spot, not everywhere they use your API, and they shouldn't be exposed to your internal dependencies. This situation screams for partial classes, but they are not available in C++. So instead, you might simply define the Impl structure in separate header files, one for D3D, and one for OGL, and put an #ifdef switch at the top of the Texture2D.cpp file, and define the rest of the public interface universally. This way, the public interface has access to the private data it needs, the only duplicate code is data member declarations (construction can still be done in the Texture2D constructor that creates the Impl), your private dependencies stay private, and users don't have to care about anything except using the limited set of calls in the exposed API surface:
// D3DTexture2DImpl.h
#include "Texture2D.h"
struct Texture2D::Impl
{
/* insert D3D-specific stuff here */
};
// OGLTexture2DImpl.h
#include "Texture2D.h"
struct Texture2D::Impl
{
/* insert OGL-specific stuff here */
};
// Texture2D.cpp
#include "Texture2D.h"
#ifdef USING_D3D
#include "D3DTexture2DImpl.h"
#else
#include "OGLTexture2DImpl.h"
#endif
Key::Texture2D Texture2D::GetKey() const
{
return m->key;
}
// etc...