Home > Back-end >  Wrap a C header in C
Wrap a C header in C

Time:06-07

Suppose I have some C code that I want to wrap using C without exposing the C code to the user.

My first attempt was to manually include all headers used in the C code before including the C headers inside a namespace, to hide them from the global scope. This seemed to initially work and compile, however, after trying to use some of the defined structures, I got compilation and linking errors since it couldn't find their definitions.

So my question is, either how do I make the above work, or how else do I make my wrapper in a way not to expose the C code to the user of the C headers.

I should mentioned, that I need the structures and other types in my declarations, so I can't include the header in the cpp file only, for example:

class Wrapper {
    c_struct* _internal;

public:
    void proxy() {
        c_function(this->_internal);
    }
}

CodePudding user response:

You could use the pimpl idiom to hide all the C headers and structs inside your .cpp file.

Example .hpp file:

#pragma once
#include <memory>

class Wrapper {
public:
    Wrapper();
    Wrapper(const Wrapper&);
    Wrapper(Wrapper&&) noexcept;
    Wrapper& operator=(const Wrapper&);
    Wrapper& operator=(Wrapper&&) noexcept;
    ~Wrapper();

    void proxy();

private:
    struct internal_type;                       // forward declaration
    std::unique_ptr<internal_type> m_internal;  // pointer to implementation
};

Example .cpp file:

#include "Wrapper.hpp"
#include all the C headers you need here

// the definition of Wrapper::internal_type may inherit from c_struct - or
// use composition:
struct Wrapper::internal_type : c_struct {
    // Don't add member variables but you could add member functions
    // that operates on the c_struct here.
};

// default constructor
Wrapper::Wrapper() : m_internal(std::make_unique<internal_type>()) {}

// copy constructor
Wrapper::Wrapper(const Wrapper& o)
    : m_internal(std::make_unique<internal_type>(*o.m_internal)) {}

// move constructor
Wrapper::Wrapper(Wrapper&& o) noexcept
    : m_internal(std::exchange(o.m_internal, nullptr)) {}

// copy assignment operator
Wrapper& Wrapper::operator=(const Wrapper& rhs) {
    *m_internal = *rhs.m_internal;
    return *this;
}

// move assignment operator
Wrapper& Wrapper::operator=(Wrapper&& rhs) noexcept {
    std::swap(m_internal, rhs.m_internal);
    return *this;
}

// destructor
Wrapper::~Wrapper() = default;

// example calling a c function that needs a `c_struct*`:
void Wrapper::proxy() { 
    c_function(m_internal.get()); 
}

I suggest that you add member functions to Wrapper::internal_type to keep everything dealing with c_struct in one place:

struct Wrapper::internal_type : c_struct {
    void proxy() {
        c_function(this);
    }
};

Then Wrapper::proxy() can simply be:

void Wrapper::proxy() {
    m_internal->proxy();
}
  •  Tags:  
  • c
  • Related