I'm trying to create a public C++ API for a private code base that is also written in C++. I tried to do this as follows:
This seemed like a reasonable approach. However, in reality it quickly results in very clumsy code. Here is an example that represents the problem I'm experiencing.
(Edit: the code can also be viewed here).
namespace Core {
// Base class for all Core classes.
class CoreObject
{
public:
virtual ~CoreObject();
};
class Interface;
class Stream;
class Server : public CoreObject
{
public:
Interface * createInterface();
private:
std::vector<Interface*> mInterfaces;
};
class Interface : public CoreObject
{
public:
void addStream(Stream * stream);
const Stream * getStreamByIndex(std::size_t index) const;
std::size_t streamCount() const;
private:
std::vector<Stream*> mStreams;
};
class Stream : public CoreObject
{
public:
void start();
void stop();
private:
std::string mStats;
};
} // Core
namespace Core {
class CoreObject;
}
namespace API {
class APIStream;
class APIInterface;
// Base class for all API classes.
class APIObject
{
public:
APIObject(Core::CoreObject * inCoreObject);
virtual ~APIObject();
Core::CoreObject * getCoreObject();
const Core::CoreObject * getCoreObject() const;
void setCoreObject(Core::CoreObject * inCoreObject);
private:
Core::CoreObject * mCoreObject;
};
class APIServer : public APIObject
{
public:
APIServer();
APIInterface * createInterface();
};
class APIInterface : public APIObject
{
public:
APIInterface();
void addStream(APIStream * stream);
const APIStream * getStreamByIndex(std::size_t index) const;
APIStream * getStreamByIndex(std::size_t index);
std::size_t streamCount() const;
};
class APIStream : public APIObject
{
public:
APIStream();
void start();
void stop();
};
#include "API.h"
#include "Core.h"
namespace API {
APIObject::APIObject(Core::CoreObject * inCoreObject) :
mCoreObject(inCoreObject)
{
}
APIObject::~APIObject()
{
}
Core::CoreObject * APIObject::getCoreObject()
{
return mCoreObject;
}
const Core::CoreObject * APIObject::getCoreObject() const
{
return mCoreObject;
}
void APIObject::setCoreObject(Core::CoreObject * inCoreObject)
{
mCoreObject = inCoreObject;
}
//
// APIServer
//
APIServer::APIServer() :
APIObject(new Core::Server)
{
}
APIInterface * APIServer::createInterface()
{
Core::Server * coreServer = static_cast<Core::Server*>(getCoreObject());
Core::Interface * coreInterface = coreServer->createInterface();
APIInterface * result(new API::APIInterface);
result->setCoreObject(coreInterface);
return result;
}
//
// APIInterface
//
APIInterface::APIInterface() :
APIObject(new Core::Interface)
{
}
void APIInterface::addStream(APIStream * apiStream)
{
Core::Stream * coreStream = static_cast<Core::Stream *>(apiStream->getCoreObject());
Core::Interface * coreInterface = static_cast<Core::Interface*>(getCoreObject());
coreInterface->addStream(coreStream);
}
//
// APIStream
//
const APIStream * APIInterface::getStreamByIndex(std::size_t index) const
{
const Core::Interface * coreInterface = static_cast<const Core::Interface*>(getCoreObject());
const Core::Stream * coreStream = coreInterface->getStreamByIndex(index);
// Now how I get the the APIStream object?
return 0;
}
std::size_t APIInterface::streamCount() const
{
const Core::Interface * coreInterface = static_cast<const Core::Interface*>(getCoreObject());
return coreInterface->streamCount();
}
APIStream::APIStream() :
APIObject(new Core::Stream)
{
}
void APIStream::start()
{
static_cast<Core::Stream*>(getCoreObject())->start();
}
void APIStream::stop()
{
static_cast<Core::Stream*>(getCoreObject())->stop();
}
} // API
As you can see the implementation looks doesn't look too good. I would appreciate your answers or insights regarding these questions:
I still have to apply this solution to the real code at work tomorrow. I'm interested to see how well it works there.
When doing this kind of thing, where there is more or less a one-to-one relation between the API and implementation objects, I'll generally use a static factory method, and have the implementation class derive from the API class. Like this:
class Interface
{
public:
static Interface* Create();
virtual void Foo() = 0;
};
class Concrete : public Interface
{
public:
void Foo() {};
};
Interface* Interface::Create()
{
return new Concrete;
}
This has a number of benefits. Including:
Create can construct many different implementations, and Interface's ABI doesn't have to change.Interface you've got.See Adapter Pattern
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With