0

I currently have two different headers defining the same class. What changes between them is the functions implementation and a single variable.

header_linux.h

class Class
{
private:
    QStringList processesList; // Same variable
    pid_t pid; // Different variable

public:
    void fillProcessesList()
    {
            // Different implementation
    }

    void isProcessRunning(QString name)
    {
            // Same implementation
    }

    template<typename T>
    bool readMemory(unsigned long long address, T &destination)
    {
            // Different implementation
    }

header_win32.h

class Class
{
private:
    QStringList processesList; // Same variable
    HANDLE handle; // Different variable

public:
    void fillProcessesList()
    {
            // Different implementation
    }

    void isProcessRunning(QString name)
    {
            // Same implementation
    }

    template<typename T>
    bool readMemory(unsigned long long address, T &destination)
    {
            // Different implementation
    }

I would like to have the common variables and functions in a single file.

My idea was to create header.h which defines the class and its members, header.cpp which implements the common functions and then header_linux.cpp and header_win32.cpp which implement the OS-specific functions.

However, template functions must be implemented in the header file.

I could check the operating system using a preprocessor macro and according to that use the right implementation, in a single header, but the functions are many and their body is big.

After a lot of research I found the PIMPL idiom, but the implementation seems complicated.

Is there a better way?

  • 3
    For what it's worth, `Pimpl` was the first thought that sprang to my mind after I read the first few sentences. – Igor Tandetnik Jun 11 '17 at 17:48
  • 1
    You should wrap your OS specific stuff into a **non** generic function, and interface with that. As far as I know, OS don't do templates – Passer By Jun 11 '17 at 17:48
  • 1
    ***but the implementation seems complicated.*** You must be reading the wrong sources. There is very little complication using PIMPL (at least to me). Although I have been programming since the early 1980s. – drescherjm Jun 11 '17 at 17:49
  • Thank you for the amazingly fast replies! Could you link me a proper example/source on how to use Pimpl? – Davide Beatrici Jun 11 '17 at 17:52
  • 2
    http://en.cppreference.com/w/cpp/language/pimpl – Passer By Jun 11 '17 at 18:04
  • 1
    Qt itself uses PIMPL internally -- see [this question](https://stackoverflow.com/questions/25250171/how-to-use-the-qts-pimpl-idiom) for details on how you can use PIMPL macros and such built in to Qt. – MrEricSir Jun 11 '17 at 18:47
  • It seems like I still have to use this for my template function: https://stackoverflow.com/questions/115703/storing-c-template-function-definitions-in-a-cpp-file – Davide Beatrici Jun 11 '17 at 19:40
  • I may have misunderstood the question, but it seems to me that you would only need to implement a non-template `readMemory(void *destination, size_t size, uintptr_t address)` for each platform in the .cpp (wrapping `ReadProcessMemory` on Windows and a `read` from `/proc/$pid/mem` on Linux); then the template helper can be implemented in a cross-platform fashion in the header. – Matteo Italia Jun 12 '17 at 06:06
  • The problem is that I can't implement it in the header, as the private class is abstract. – Davide Beatrici Jun 12 '17 at 13:53

1 Answers1

1

You can write template implementations in cpp files, all you have to do is write an explicit instantiation definition after the template implementation in the cpp file.

http://en.cppreference.com/w/cpp/language/class_template#Explicit_instantiation

Example:

header.h

template <typename T>
bool readMemory(unsigned long long address, T &destination);

implementation.cpp

template <typename T>
bool readMemory(unsigned long long address, T &destination)
{
    // Implementation
}

// Explicit definitions
template bool readMemory<std::string>(unsigned long long address, std::string &destination);
template bool readMemory<int>(unsigned long long address, int &destination);

In this example, you can use readMemory only with std::string and int template arguments. Therefore, you have to write a definition for every typename that will be used in the template, or else you will get a linker error. This obviously reduces scalability of your solution, but if there will only be a few possible template arguments, then it should be acceptable.

snoopy
  • 320
  • 1
  • 12