0

I wrote a C++ wrapper class for a C API.

My wrapper class has a private instance member of a typedef structure used in the original API. I do not want to modify the API header file, but I also want to hide all traces of the original API, so I DO NOT want to include the original API header file in the wrapper class' header.

I'm probably overlooking something really simple, but I'm not finding it. PIMPL is an option, but I'd prefer to seek alternatives before refactoring the whole codebase for one little thing.

wrapper.hpp

class myWrapper
{
public:
    myWrapper();

private:
    originalTypedef *original; // forward declaration needed for originalTypedef 
};

wrapper.cpp

#include "wrapper.hpp"
#include "originalAPI.h"

myWrapper::myWrapper()
{
    original = originalAPI_get();
}

Main.cpp

#include "wrapper.hpp"

int main()
{
    myWrapper wrapper = new myWrapper();
}
audiFanatic
  • 2,014
  • 5
  • 27
  • 50
  • 2
    Can you provide us with a [MCVE] please? – πάντα ῥεῖ Aug 10 '16 at 22:12
  • Aside what I've provided? What I've provided is more coherent. – audiFanatic Aug 10 '16 at 22:13
  • [PIMPL.](http://stackoverflow.com/questions/60570/why-should-the-pimpl-idiom-be-used) – user4581301 Aug 10 '16 at 22:14
  • @user4581301 It's an option, but I'd like to seek alternatives before refactoring the code to implement PIMPL. – audiFanatic Aug 10 '16 at 22:15
  • That's not a [MCVE] of course! Please put at least a link to a decent online compiler. – πάντα ῥεῖ Aug 10 '16 at 22:17
  • Why is "typedef" significant? It's not one of those "typedef of an untagged struct" things that some C programmers seem to believe is a good idea? – molbdnilo Aug 10 '16 at 22:22
  • @πάνταῥεῖ Happy? I've included a `main` I'm not including the original C typedef because it's irrelevant to the question. In fact, I shouldn't include it because that's the whole point, I want to hide it. – audiFanatic Aug 10 '16 at 22:24
  • PIMPL seems like the most obvious way to do this, though. I think you're saying you don't want to use pointers because you'd have to change the rest of the file? I'm not sure if something like `extern struct originalTypedef` would work...? – Keith M Aug 10 '16 at 22:25
  • @molbdnilo I believe it is. It's declared as follows: `struct hid_device_;` `typedef struct hid_device_ hid_device; /**< opaque hidapi structure */` – audiFanatic Aug 10 '16 at 22:28
  • If you use a `typedef` in the header, you will have to expose the `typedef` in the header. You can tag it private, but you're stuck including the original api header somewhere. If you really want to hide the original API, it's `void *`, PIMPL, or PIMPL by another name. – user4581301 Aug 10 '16 at 22:33

3 Answers3

3

Options that I was able to think of quickly:

  1. You can repeat just the typedef from the C API in myWrapper.h.

    typedef struct the_original_struct_type originalTypedef;
    
  2. You can use a void* as the member variable in myWrapper and reinterpret_cast to originalTypedef* before calling the C API.

    class myWrapper
    {
       private:
          void* original;
    };
    
R Sahu
  • 196,807
  • 13
  • 136
  • 247
  • Was writing this answer but this was first. Quick note that option (1) is only appropriate if the C API is not expected to change in the future (as then you'll have to update the wrapper). Also you may need to use a conditional macro "IS_ORIGINAL_C_API_INCLUDED" or such to prevent multiple definition errors in myWrapper.cpp. – Ben Braun Aug 10 '16 at 22:26
  • #2 will work but you'll lose all type information, so careful with the casting. – JBRWilkinson Aug 10 '16 at 22:48
1

An exotic solution would be to write a code generator that reads in original_c_api.h, and outputs a file original_c_api_types.h that only includes struct definitions, types, etc but does not include any functions, and a file original_c_api_functions.h containing the function declarations. Then wrapper.h includes original_c_api_types.h and wrapper.cpp includes wrapper.h and original_c_api_functions.h. The code generator should be run as a target in your makefile.

Of course, if you have the authority to modify the original C api files that's the easiest solution.

Ben Braun
  • 419
  • 3
  • 10
0

What you're looking for is something like the pImpl pattern - you provide a C++ API and the implementation is obfuscated from the client:

Header File

// forward declare obfuscated implementation
// we can do this because we're only using a ptr
class Implementation;

class Wrapper
{
   public:
      Wrapper();
      ~Wrapper();
      void doSomething();

   private:
      Implementation * pImpl;
  };

.cpp file

  #include "originalAPI.h"
  class Implementation
  {
     public:
      void doSomething()
      {
         OriginalAPI_DoSomething();
      }
   };

   Wrapper::Wrapper()
   {
     this->pImpl = new Implementation();
   }

   Wrapper::~Wrapper()
   {
      delete pImpl;
   }

   void Wrapper::doSomething()
   {
      pImpl->doSomething;
   }
JBRWilkinson
  • 4,705
  • 1
  • 23
  • 31
  • I appreciate the answer, but I'm looking for an alternative to PIMPL. The codebase already exists and doing such requires a massive refactor for one tiny thing – audiFanatic Aug 10 '16 at 22:32
  • okay, so just forward declare the originalTypeDef as it's exact type - but don't define it in the header. In this way, you won't need to include the original api header in your wrapper header. – JBRWilkinson Aug 10 '16 at 22:49