Home > Mobile >  C class implementation with variable types from other header files
C class implementation with variable types from other header files

Time:09-23

Given this current setup, how can I implement this so that I can avoid including any header files in the file.h header file?

file.h:

#ifndef __FILE_H__
#define __FILE_H__

class foo {
    private:
        Vector vector; // std::vector from <vector>
        Map map; // std::map from <map>
    public:
        void bar();
};

#endif

file.cpp:

#include "file.h"

void foo::bar()
{
    // Do something with private variables...
}

main.cpp

#inclue "file.h"

int main()
{
    foo var;
    // Do something...
}

CodePudding user response:

Given this current setup, how can I implement this so that I can avoid including any header files in the file.h header file?

Member variables of a class must be defined before definition of the class. You can avoid including any files by defining everything in the file.h file.

Standard types are only defined in standard headers and you cannot avoid including those if you want to use them as members. As such, if you want to avoid including headers, then you have to avoid using standard library.

I suppose you could also avoid including headers by importing modules instead. Standard library isn't modular yet though.

CodePudding user response:

Abstract class or or pimpl-idiom might avoid to have extra #include:

  • Abstract class:

    #include <memory>
    
    class fooInterface
    {
    public:
        virtual ~fooInterface() = default;
        void bar() = 0;
    };
    
    std::unique_ptr<fooInterface> make_foo();
    

    with foo remains the same, but in cpp files with additional

    std::unique_ptr<fooInterface> make_foo() { return std::make_unique<foo>(); }
    
  • pimpl-idiom:

    #include <memory>
    
    class foo
    {
        struct Impl;
        std::unique_ptr<Impl> impl;
    public:
        ~foo();
        void bar();
    };
    

    and in cpp file:

    #include <map>
    #include <vector>
    
    class foo::Impl // Roughly your current `foo`
    {
    private:
        Vector vector; // std::vector from <vector>
        Map map; // std::map from <map>
    public:
        void bar() { /**/}
    };
    
    foo::~foo() = default; // Here for `foo::~Impl` which should be visible
    void foo::bar() { return impl->bar(); }
    

CodePudding user response:

The motivation for doing something like this would be to reduce compilation times by minimising include trees (in this case "file.h" would be pulled in from lots of places, and it will also in turn, pull in lots of other includes). It also improves testability/mockability. Due to the additional complexity, I would only be inclined to use this for "domain level" objects which are accessed via an API (i.e. do not use this where there is sparse usage).

You can effect this by abstracting foo such that only its API is exposed in the header file. You then implement a subclass which pulls in the includes - but they are only pulled in once.

For more detail I would consult "Large-Scale C Software Design" from John Lakos. Allegedly the older version is better: https://www.amazon.co.uk/Large-Scale-C-Software-Design-APC/dp/0201633620

He has also spoken at various conferences about the new "module" feature: e.g. https://www.youtube.com/watch?v=EglLjioQ9x0

As a bare minimum example you would have the following. Obviously it would be better to return a unique_ptr<foo>.

file.h

#ifndef _FILE_H_672b47fa_7d02_458a_bd42_4774a3765a1b
#define _FILE_H_672b47fa_7d02_458a_bd42_4774a3765a1b

struct foo {
        virtual ~foo() = default;
        virtual void bar() = 0;
};

extern foo* fooFactory();

#endif

file.cpp

#include "file.h"
#include <...etc...>
...etc...

class fooImpl : public foo {
    private:
        Vector vector; // std::vector from <vector>
        Map map; // std::map from <map>
        ...etc...
    public:
        virtual ~fooImpl() = default;
        void bar() override;
};


void fooImpl::bar() {
    // Do something with private variables...
}

foo* fooFactory() {
    return new fooImpl();
}

main.cpp

#include "file.h"

int main() {
    foo* = fooFactory();
    // Do something...
    delete foo;
}

An alternative approach, as others have pointed out, would be to encapsulate a separate struct containing the "data" via a member pointer. It's a valid approach although it could make const-correctness harder to enforce. It's also related to the flyweight pattern which is useful when the data needs to be passed around different objects, see https://refactoring.guru/design-patterns/flyweight

CodePudding user response:

You generally can if you instead always include the standard header from the C file that uses the class:

#include <vector>
#include <custom_stuff_that_references_std_vector.h>

The definitions from the previously included files remain valid.

Looks like a useful feature to add to the code obfuscator.

CodePudding user response:

You may take a look at this first. What are forward declarations in C ?

private.h

struct fooPrivate
{
Vector vec;
Map m;
}

file.h

struct fooPrivate;
class foo
{
private:
    fooPrivate *m_ptr;
};

file.cpp

#include "private.h"
m_ptr = new fooPrivate();  // In constructor

You can avoid including any header files in the file.h, but you still need to include stl headers in private.h.

  •  Tags:  
  • c
  • Related