recently we encountered in our legacy code that is currently ported from VS2010 to VS2015 an interesting effect. Unfortunately I couldn't create a small example that shows this effect, but I'll try to describe it as accurately as I can.
We have 2 dlls (I'll call them dll A and dll B). The project for dll A defines the interface IFoo & a derived interface IFxFoo
class __declspec(novtable) IFoo {
public:
virtual int GetType() = 0;
virtual ~IFoo() {}
};
class __declspec(novtable) IFxFoo : public IFoo {
public:
virtual int GetSlot() = 0;
};
in dll B, both interfaces are used.
class CBImpl : public IFxFoo {
public:
...
void processFoo(IFoo* f) {
...
if (f->GetType() == IFXFOO) {
IFxFoo* fx = static_cast<IFxFoo>(f); //downcast
fill(fx);
}
}
void fill(IFxFoo* fx) {
m_slot = fx->GetSlot();
}
private:
int m_slot;
};
processFoo() will be called with different implementations of IFoo. Some from dll A and some from dll B.
What now happened was the following:
- if we turned on whole program optimization when compiling dll B, the call to the virtual function GetSlot() in function fill() got de-virtualized by Visual C++. This caused our program to crash.
we can fix this behavior if we either
turn of whole program optimization
turn of optimization for fill
or mark our interfaces with __declspec(dllimport) / __declspec(dllexport)
The questions that I have now are:
is our assumption correct that the de-virtualization happened because the optimizer saw only one implementation of IFxFoo in dll B and assumed that this is the only one because IFxFoo was not marked to be from a different dll?
what's the best way to create "interfaces" in header files? We used to do them like above but this seems to lead to some problems.
do other compiler (gcc / clang) exhibit similar behavior?
Thank you for your help
Tobias
Using LTO results in the compiler making drastic adjustments to any functions for which is it able to see the complete callgraph.
What you are seeing is expected and using __declspec(dllexport) or extern on the functions that need to be utilised from a separate module or explicitly declaring them as part of a DLL .def file is the expected way to resolve the problem as the compiler will no longer consider the functions to be internal-only.
I am trying to figure out exactly how constructors for global objects are called. I understand that they are called before anything in a translation unit is used, and I am fine with that. I am trying to find out how in Linux and Windows (x86 and x64) this is accomplished.
I seem to remember that Windows (x86) used a linked list for construction and destruction, but I am having trouble finding any resources on this matter.
I have found the following material on related topics, but nothing seems to cover exactly what I am looking for.
http://blogs.msdn.com/b/freik/archive/2005/03/17/398200.aspx
http://msdn.microsoft.com/en-us/library/9b372w95.aspx
http://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx
And the PE file format document.
Could anyone point me in the correct direction to find this information?
Just in case your failing to understand I have code here to demonstrate.
SourceA.cpp
#include "stdafx.h"
extern bool DoFunctionB();
class MyClassA {
protected:
bool bIsInitialized;
bool bIsBInitialized;
public:
MyClassA () : bIsInitialized(true) {
bIsBInitialized = DoFunctionB();
}
bool IsInitialized() {
return bIsInitialized;
}
};
static MyClassA MyClassGlobal;
bool DoFunctionA() {
return MyClassGlobal.IsInitialized();
}
SourceB.cpp
#include "stdafx.h"
extern bool DoFunctionA();
class MyClassB {
protected:
bool bIsInitialized;
bool bIsAInitialized;
public:
MyClassB () : bIsInitialized(true) {
bIsAInitialized = DoFunctionA();
}
bool IsInitialized() {
return bIsInitialized;
}
};
static MyClassB MyClassGlobal;
bool DoFunctionB() {
return MyClassGlobal.IsInitialized();
}
Main.cpp
#include "stdafx.h"
extern bool DoFunctionA();
extern bool DoFunctionB();
int _tmain(int argc, _TCHAR* argv[])
{
bool a = DoFunctionA();
bool b = DoFunctionB();
return 0;
}
Add these to a new windows console app. Place breakpoints in the constructors, and in the DoFunctionX() code. Hit F11 and step through it. You will see that whichever global initializer gets called first will use the DoFunction in the other cpp file before the static object in that file gets initialized.
Regardless of what you think the standard may be. This is what compilers do. And its a hazard that you have to be concerned with.
And if you step up the stack 2 steps when your in the constructor you will see the list of pointers that I've already told you about.
Happy Coding.
You are wrong to think that global constructors must be run before the object is used. I've fixed many a bug based on this assumption and it simply is not true. Not for gcc, and not for MSVC, abd certainly not for XCode.
You can specify an attribute((init_priority(X))) in gcc to force the order,
or #pragma init_seg({ compiler | lib | user | "section-name" [, func-name]} ) for msvc.
When working with XCode, the initialization code is run in the order that the object files are passed to the linker.
I dont think there is a standard, and if there is then very few people are following it. Its up to the tools creator to decide how they want to keep track of whats getting initialized and when.
I'm racking my brain trying to find out how to write cross platform classes while avoiding the cost of virtual functions and any kind of ugliness in the platform specific versions of classes. Here is what I have tried.
PlatformIndependantClass.hpp
class PlatformIndependantClass {
public:
PlatformIndependantClass();
std::string GetPlatformName();
private:
PlatformIndependantClass* mImplementation;
};
LinuxClass.hpp
#include "PlatformIndependantClass.hpp"
class LinuxClass : public PlatformIndependantClass{
public:
std::string GetPlatformName();
};
WindowsClass.hpp
#include "PlatformIndependantClass.hpp"
class WindowsClass : public PlatformIndependantClass {
public:
std::string GetPlatformName();
};
PlatformIndependantClass.cpp
#include "PlatformIndependantClass.hpp"
#include "LinuxClass.hpp"
#include "WindowsClass.hpp"
PlatformIndependantClass::PlatformIndependantClass() {
#ifdef TARGET_LINUX
mImplementation = new LinuxClass();
#endif
#ifdef TARGET_WINDOWS
mImplementation = new WindowsClass();
#endif
}
std::string PlatformIndependantClass::GetPlatformName() {
return mImplementation->GetPlatformName();
}
LinuxClass.cpp
#include "LinuxClass.hpp"
std::string LinuxClass::GetPlatformName() {
return std::string("This was compiled on linux!");
}
WindowsClass.cpp
#include "WindowsClass.hpp"
std::string WindowsClass::GetPlatformName() {
return std::string("This was compiled on windows!");
}
main.cpp
#include <iostream>
#include "PlatformIndependantClass.hpp"
using namespace std;
int main()
{
PlatformIndependantClass* cl = new PlatformIndependantClass();
cout << "Hello world!" << endl;
cout << "Operating system name is: " << cl->GetPlatformName() << endl;
cout << "Bye!" << endl;
return 0;
}
Now, this compiles fine but I get a segmentation fault. I believe this is because the platform specific classes inherit from PlatformIndependantClass, which on construction, creates an instance of the platform specific class, so I get infinite recursion. Every time I try, I just get extremely confused!
How can I achieve a design like this properly? Or is this just a horrible idea. I have been trying to find out how to write cross platform classes but I just get a load of results about cross platform libraries, any help will be gratefully accepted :)
I think what you are trying to accomplish can be accomplished much easier...
Object.h:
#include <normal includes>
#if WINDOWS
#include <windows includes>
#endif
#if LINUX
#include <linux includes>
#endif
class Object
{
private:
#if WINDOWS
//Windows Specific Fields...
#endif
#if LINUX
//Linux Specific Fields...
#endif
public:
//Function that performs platform specific functionality
void DoPlatformSpecificStuff();
//Nothing platform specific here
void DoStuff();
};
Object.cpp
#include "Object.h"
void Object::DoStuff() { ... }
ObjectWin32.cpp
#if WINDOWS
#include "Object.h"
void Object::DoPlatformSpecificStuff()
{
//Windows specific stuff...
}
#endif
ObjectLinux.cpp
#if LINUX
#include "Object.h"
void Object::DoPlatformSpecificStuff()
{
//Linux specific stuff...
}
#endif
And so on. I think this could accomplish what you are trying in a bit easier fashion. Also, no virtual functions needed.
Starting from the end, yes, truly a horrible idea, as are most ideas that start with "I want to avoid the cost of virtual functions".
As to why you're getting the segmentation fault (stack overflow specifically), it's because you aren't using virtual functions, but static linking. The compiler doesn't know that mImplementation is anything but a PlatformIndependantClass, so when you try to call return mImplementation->GetPlatformName() you're calling the same function over and over.
What you achieved is called shadowing, you're using compile-time function resolution. The compiler will call the GetPlatformName function of the actual type of the variable you're calling it from, since there's no virtual table to overwrite the pointers to the actual functions. Since mImplementation is PlatformIndependantClass, mImplementation->GetPlatformName will always be PlatformIndependantClass::GetPlatformName.
Edit: Of course the question of why you need to create both a Windows and a Linux copy of your engine at the same time comes to mind. You'll never use both of them at the same time, right?
So why not just have two different libraries, one for each system, and link the right one from your makefile. You get the best of all worlds!
Instead of using the constructor to build the platform-specific instance, I would create a static factory method to create the instances:
PlatformIndependantClass* PlatformIndependantClass::getPlatformIndependantClass() {
#ifdef TARGET_LINUX
return new LinuxClass();
#endif
#ifdef TARGET_WINDOWS
return new WindowsClass();
#endif
}
This way you avoid the recursion, and you also don't need your mImplementation pointer.
I would also try to avoid platform-specific classes, but that's another story :)
When you want to have polymorphic behavior without any run-time overhead, you can try the curiously recurring template pattern (CRTP). The base class is a template, and the derived class uses itself as the template parameter for the base. This requires your classes to be defined as templates, which further restricts them to be implemented completely in the header (.hpp) files.
I'm not sure how to apply the pattern in your particular case.
I don't think the constructor is causing the infinite recursion. It's the GetPlatformName() function. Because it's not set as virtual, it can only call itself.
Two solutions: Make that function virtual, or do away with the inheritance completely.
Either way, the cost of a function only calling another function will be more expensive than using virtual functions in the first place. So I would say keep the inheritance, and virtualize the functions specific to the platform, and call them directly, without going through a base class function.
You are correct about the infinte loop. The fix is actually easier than you'd think.
PlatformIndependantClass.hpp
#include //portable headers
struct PlatformDependantClass; //defined in Cpp file
class PlatformIndependantClass {
public:
PlatformIndependantClass();
~PlatformIndependantClass();
std::string GetPlatformName();
private:
std::unique_ptr<PlatformDependantClass> mImplementation; //note, different type
};
LinuxClass.cpp
#ifdef __GNUC__
#include //linux headers
#include "PlatformIndependantClass.hpp"
struct PlatformDependantClass { //linux only stuff
//stuff
};
PlatformIndependantClass() {
mImplementation.reset(new PlatformDependantClass );
}
~PlatformIndependantClass() {
}
std::string PlatformIndependantClass::GetPlatformName() {
return std::string("This was compiled on linux!");
}
#endif //__GNUC__
WindowsClass.cpp
#ifdef _MSC_VER
#include //windows headers
#include "PlatformIndependantClass.hpp"
struct PlatformDependantClass { //windows only stuff
//stuff
};
PlatformIndependantClass() {
mImplementation.reset(new PlatformDependantClass );
}
~PlatformIndependantClass() {
}
std::string PlatformIndependantClass::GetPlatformName() {
return std::string("This was compiled on Windows!");
}
#endif //_MSC_VER
There's only ONE class defined here. In windows, it only compiles and contains windows stuff, and in Linux, it only compiles and contains linux stuff. Note that the void* thing is called an "Opaque pointer" or "pimpl idiom" http://en.wikipedia.org/wiki/Opaque_pointer
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...