0

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?

cf-
  • 8,598
  • 9
  • 36
  • 58
  • 1
    Can't you declare those fields protected instead of private? This should let you "overwrite" them – BlackBear Apr 03 '14 at 19:50
  • @BlackBear I just tried changing the data members to protected in both `ICommandImplementation_v1` and `AboutCommand`, but I'm still getting the same problem. – cf- Apr 03 '14 at 19:53

2 Answers2

2

The easiert way is to pass the two strings to the base class as parameters:

struct ICommandImplementation_v1: ICommand_v1
{
public:
  ICommandImplementation_v1(wchar_t const* name, wchar_t const* guid):
     commandName(name),
     commandGUID(guid)
  {
  }
  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);
  }

  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");
};

struct AboutCommand: ICommandImplementation_v1
{
public:
  AboutCommand(): 
      ICommandImplementation_v1(TEXT("AboutCommand"), 
                                TEXT("01eba0e6-81b9-4fa7-a9f3-407d330da9b3")) 
  {}

  bool Execute(int UNUSED(argc), wchar_t* UNUSED(argv[]))
  {
    return true;
  }
};

In general, what you want to is called "template method". Instead of defining two private fields in the base clase, define two private pure virtual methods which get called GetCommandGUID and GetCommandName and return the subclass-specific value:

struct ICommandImplementation_v1: ICommand_v1
{
public:
  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());
  }

  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:
  virtual const wchar_t* commandName() = 0;
  virtual const wchat_t* commandGUID() = 0;
};

struct AboutCommand: ICommandImplementation_v1
{
public:
  bool Execute(int UNUSED(argc), wchar_t* UNUSED(argv[]))
  {
    return true;
  }

private:
  const wchar_t* commandName() {return TEXT("AboutCommand")}
  const wchar_t* commandGUID() {TEXT("01eba0e6-81b9-4fa7-a9f3-407d330da9b3")}
};

And please do yourself the favour and do not use wchar_t* but use std::wstring for strings.

Jens
  • 9,058
  • 2
  • 26
  • 43
  • Thanks. Should've occurred to me to use the base class's c'tor for this. I can't use `std::wstring` for this, though, since this code is for a DLL [and passing STL types across DLL boundaries is a bad idea](http://stackoverflow.com/questions/22797418/how-do-i-safely-pass-objects-especially-stl-objects-to-and-from-a-dll). – cf- Apr 03 '14 at 20:03
0

One of the ways I can think of is using Virtual Getters

 struct ICommandImplementation_v1: ICommand_v1
 {
  public :
   virtual char * getCommand()
   {
      return commandName;
   }
 }

struct AboutCommand: ICommandImplementation_v1
 {
    public :
     virtual char * getCommand()
       {
          return commmandName; //please take care of memory allocations etc
        }
  .
  .
 }

And then Inside the function GetCommandName() in the base class ICommandImplementation_v1 whereever you are using commandName you use this->getCommand()

for example : replace

  *nameBufferSize = wcslen(commandName);

with

  *nameBufferSize = wcslen(this->getCommand());    

So at runtime correct commandName is used depending upon the underlying object.

Also , Read about static polymorphism. I think its wastage of resources to store an extra pointer in derived class which is serving the same purpose. All you have to do is change the value of commandName in base class so you do something like the following.

class Derived1 : Base<commandName1> {
}

class Derived2 : Base<commandName2> {
}
Ravi
  • 255
  • 3
  • 16