Search code examples
c++header-files

How do I expose an enum to users of my library in a public header file but also use it internally?


If I am creating a library that exposes one public header file, which may take or return a publicly exposed enum, how would I use that enum internally without also creating a circular dependency in header files?

For example :

Public.h

#include "Internal.h"


namespace PublicFunctions {
    enum Access{
        READ,
        WRITE,
    }

    FileObject CreateFileObject(Access a) {
        return InternalFunctions::GetFileObject(a);
    }
}

Internal.h

#include "Public.h"


namespace InternalFunctions {

    FileObject GetFileObject(PublicFunctions::Access a);
}

Internal.cpp

FileObject InternalFunctions::GetFileObject(PublicFunctions::Access a) {
    if (a == PublicFunctions::Read) {
          return openreadonly();
    }
    else {
              return openwrite();
    }
}

I know about the #praga once preprocessor directive, but is there any way to forward declare an enum in these internal files ?

Or is #pragma once the best method to resolve these dependencies ?


Solution

  • An include guard cannot help you because if public.h is included first, the logical case for the library's clients, you will wind up with

    // public.h already included so #include "Public.h" is ignored 
    namespace InternalFunctions {
    
        FileObject GetFileObject(PublicFunctions::Access a);
        // PublicFunctions::Access not defined yet.
    }
    
    namespace PublicFunctions {
        enum Access{
            READ,
            WRITE,
        }
    
        FileObject CreateFileObject(Access a) {
            return InternalFunctions::GetFileObject(a);
        }
    }
    

    Worse anyone who includes public.h gets a peek into internals.h, which defeats the point of separating the two.

    But if you remove CreateFileObject's implementation from public.h you can

    namespace PublicFunctions {
        enum Access{
            READ,
            WRITE,
        }
    
        FileObject CreateFileObject(Access a);
    }
    

    and leave internal.h alone. It will be used exclusively by the compiled library and not be shipped with the public interface.

    #include "Public.h"
    
    namespace InternalFunctions {
    
        FileObject GetFileObject(PublicFunctions::Access a);
    }
    

    and then Internal.cpp

    #include "internal.h"
    FileObject InternalFunctions::GetFileObject(PublicFunctions::Access a) {
        if (a == PublicFunctions::Read) {
              return openreadonly();
        }
        else {
                  return openwrite();
        }
    }
    

    and finally public.cpp

    #include "internal.h"
    FileObject PublicFunctions::CreateFileObject(Access a) {
            return InternalFunctions::GetFileObject(a);
        }
    

    Whether it is worth separating public.cpp and internal.cpp is up to the programmer and their coding standard.