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 struct
s 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();
}