Hiding Implementation Details in C++

1

I would like to hide implementation specific details from the interfaces defined in the header so the code is maintainable and quicker to compile when making updates (although I don't have statistics for the latter).

However, I cant use dynamic memory or c++ past 2003 (no 11/14/17). I also cant use libraries like boost. The application is real-time embedded and thus efficiency matters. The code in question is tightly coupled to the hardware.

I looked at several posts on here which suggested PIMPL idiom, however indirection and dynamic memory would seem to prohibit this. One option is to preallocate the classes and set the pointer but this seems inflexible.

Below is an oversimplified example of just using functions and static variables in the implementation file; are there problems related to encapsulation or the compile process? How else could I achieve the above given the constraints?

Example with detail in header:

#ifndef HARDWARE_IF_HPP
#define HARDWARE_IF_HPP

class hardware_if
{
public:
    enum cfg_mode { standby, enable, disable };
    void configure(cfg_mode mode);

private:
    void hardware_if_standby();
    void hardware_if_enable();
    void hardware_if_disable();

    static const uint32_t CONTROL_REGISTER = 0x10000000;
    static const uint32_t ENABLED_MODE     = 2;
    static const uint32_t DISABLED_MODE    = 3;
};

#endif


// CPP

#include <cstdio>
#include <stdint.h>
#include "hardware_if.hpp"

void hardware_if::hardware_if_standby()
{
    printf("set hardware into standby state; write %X to 0x%X\n",
        STANDBY_MODE, CONTROL_REGISTER);
}

void hardware_if::hardware_if_enable()
{
    printf("set hardware into enabled state; write %X to 0x%X\n",
        ENABLED_MODE, CONTROL_REGISTER);
}

void hardware_if::hardware_if_disable()
{
    printf("set hardware into disabled state; write %X to 0x%X\n",
        DISABLED_MODE, CONTROL_REGISTER);
}

void hardware_if::configure(cfg_mode mode)
{
    switch (mode)
    {
    case standby: 
        hardware_if_standby();
        break;
    case enable: 
        hardware_if_enable();
        break;
    default: 
        hardware_if_disable();
        break;
    }
}


//

#include <stdint.h>
#include "hardware_if.hpp"

int main()
{
    hardware_if hdw;
    hdw.configure(hardware_if::enable);
    return 0;
}

Example with detail in implementation:

#ifndef HARDWARE_IF_HPP
#define HARDWARE_IF_HPP

class hardware_if
{
public:
    enum cfg_mode { standby, enable, disable };
    void configure(cfg_mode mode);
};

#endif


// CPP

#include <cstdio>
#include <stdint.h>
#include "hardware_if.hpp"

static const uint32_t CONTROL_REGISTER = 0x10000000;
static const uint32_t STANDBY_MODE     = 1;
static const uint32_t ENABLED_MODE     = 2;
static const uint32_t DISABLED_MODE    = 3;

void hardware_if_standby();
void hardware_if_enable();
void hardware_if_disable();

void hardware_if_standby()
{
    printf("set hardware into standby state; write %X to 0x%X\n",
        STANDBY_MODE, CONTROL_REGISTER);
}

void hardware_if_enable()
{
    printf("set hardware into enabled state; write %X to 0x%X\n",
        ENABLED_MODE, CONTROL_REGISTER);
}

void hardware_if_disable()
{
    printf("set hardware into disabled state; write %X to 0x%X\n",
        DISABLED_MODE, CONTROL_REGISTER);
}

void hardware_if::configure(cfg_mode mode)
{
    switch (mode)
    {
    case standby: 
        hardware_if_standby();
        break;
    case enable: 
        hardware_if_enable();
        break;
    default: 
        hardware_if_disable();
        break;
    }
}

//

#include <stdint.h>
#include "hardware_if.hpp"

int main()
{
    hardware_if hdw;
    hdw.configure(hardware_if::enable);
    return 0;
}
c++
pimpl-idiom
method-hiding
asked on Stack Overflow Jul 18, 2019 by electronpygmy • edited Jul 18, 2019 by electronpygmy

1 Answer

3

Your particular example class doesn't have any data members to hide. The private static data members can be safely moved into the .cxx file into an unnamed namespace, the same applies to the non-public member functions.

But here is an example how to hide the data members without memory allocations:

// hardware_if.h
class hardware_if
{
public:
    hardware_if();
    ~hardware_if();

    enum cfg_mode { standby, enable, disable };
    void configure(cfg_mode mode);

    struct Impl;

private:
    hardware_if(hardware_if const&);
    hardware_if& operator=(hardware_if const&);

    enum { IMPL_SIZE = 16 };
    union {
        unsigned char storage[IMPL_SIZE];
        double align;
    };
};

// hardware_if.cxx
#include <new>

struct hardware_if::Impl {
    // The data members.
    int a, b, c, d;
};

namespace {

const uint32_t CONTROL_REGISTER = 0x10000000;
const uint32_t ENABLED_MODE     = 2;
const uint32_t DISABLED_MODE    = 3;

void hardware_if_standby(hardware_if::Impl&) { /* ... */ }
void hardware_if_enable(hardware_if::Impl&) { /* ... */ }
void hardware_if_disable(hardware_if::Impl&) { /* ... */ }

// Compiler error if IMPL_SIZE != sizeof(hardware_if::Impl).
inline hardware_if::Impl& get_impl(unsigned char(&storage)[sizeof(hardware_if::Impl)]) {
    return reinterpret_cast<hardware_if::Impl&>(storage);
}

}

hardware_if::hardware_if() {
    new (storage) Impl;
}

hardware_if::~hardware_if() {
    get_impl(storage).~Impl();
}

void hardware_if::configure(cfg_mode mode) {
    Impl& impl = get_impl(storage);
    switch(mode) {
        case standby:
            return hardware_if_standby(impl);
        // ...
        default:
            break;
    }
}
answered on Stack Overflow Jul 18, 2019 by Maxim Egorushkin • edited Jul 18, 2019 by Maxim Egorushkin

User contributions licensed under CC BY-SA 3.0