I'm working on a C++ library. This library will contain a bunch of simple objects, all very similar to each other.
I've defined a simple interface for my objects:
struct ICommand_v1
{
virtual void GetCommandName(wchar_t* nameBuffer, size_t* nameBufferSize) = 0;
virtual void GetCommandGUID(wchar_t* guidBuffer, size_t* guidBufferSize) = 0;
virtual bool Execute(int argc, wchar_t* argv[]) = 0;
};
My difficulty here lies in the GetCommandName
and GetCommandGUID
functions. They're supposed to be almost identical for each subclass; there's a bit of sanity checking, then the command's built-in name or GUID is returned. The only difference from one GetCommandName
to another, or one GetCommandGUID
to another, is the returned value. I'd rather not duplicate these methods across the two dozen or so objects my library will be working with, so I tried making a base class for my objects:
struct ICommandImplementation_v1: ICommand_v1
{
public:
virtual void GetCommandName(wchar_t* nameBuffer, size_t* nameBufferSize)
{
size_t workingBufferSize = *nameBufferSize;
if ((nameBuffer == nullptr) || (wcslen(commandName) > workingBufferSize))
{
*nameBufferSize = wcslen(commandName);
return;
}
wcsncpy(nameBuffer, commandName, workingBufferSize);
*nameBufferSize = wcslen(commandName);
}
virtual void GetCommandGUID(wchar_t* guidBuffer, size_t* guidBufferSize)
{
size_t workingBufferSize = *guidBufferSize;
if ((guidBuffer == nullptr) || (wcslen(commandGUID) > workingBufferSize))
{
*guidBufferSize = wcslen(commandGUID);
return;
}
wcsncpy(guidBuffer, commandGUID, workingBufferSize);
*guidBufferSize = wcslen(commandGUID);
}
virtual bool Execute(int argc, wchar_t* argv[])
{
return true;
}
private:
const wchar_t* commandName = TEXT("Default");
const wchar_t* commandGUID = TEXT("Default");
};
Then, I tried to have my objects override the base class's commandName
and commandGUID
properties:
struct AboutCommand: ICommandImplementation_v1
{
public:
bool Execute(int UNUSED(argc), wchar_t* UNUSED(argv[]))
{
return true;
}
private:
const wchar_t* commandName = TEXT("AboutCommand");
const wchar_t* commandGUID = TEXT("01eba0e6-81b9-4fa7-a9f3-407d330da9b3");
};
Of course, this didn't actually work. Creating an AboutCommand
object and calling its GetCommandName
returned "Default". This makes sense - we're actually calling ICommandImplementation_v1
's GetCommandName
, and ICommandImplementation_v1
doesn't know about AboutCommand
or its shadowed commandName
.
At this point, I'm thinking I'll probably have to create a simple protected method within ICommandImplementation_v1
that takes a command name as well as a buffer/buffer size (ICommandImplementation_v1::GetRealCommandName(const wchar_t* commandName = TEXT("Default"), wchar_t* nameBuffer, size_t* nameBufferSize)
, and then AboutCommand
's GetCommandName
would just call this function with its own commandName
. This seems clumsy to me, though; is there any cleaner, more elegant way to do this?