1

I am working on a project in Visual Studio and I decided to reorganize a lot of my code. Unfortunately, I am running into problems with linking different headers and their cpp files.

I have one header file that contains the libraries which get passed down a chain of header files. I have no idea how this works, but I am able to get the contents to populate throughout all of the headers:

/* vkInstance.h */
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
/* code used other header files */
/* vkGraphicsPipeline.h */
#include "vkInstance.h" // gains access to <GLFW/glfw3.h> within "vkInstance.h"

#include "component.h" // gets access to <GLFW/glfw3.h> through #include "vkInstance.h"
/* other headers similar to component.h */

When the component header's content definitions are within the header file, elements of the <GLFW/glfw3.h> file are defined. The component header also has some macro definitions:

/* component.h */
#ifndef hComponent
#define hComponent

struct exampleStruct {
    exampleStruct(/* args */) {
        /* constructor code containing <GLFW/glfw3.h> items */
    }
    void exampleFunc(/* args */) {
        /* func code containing <GLFW/glfw3.h> items */
    }
}

#endif

While the above usage of <GLFW/glfw3.h> items is defined, I would like to move a lot of the definitions from my various header files to their cpp files, and that's where I start to run into problems:

/* component.h */
#ifndef hComponent
#define hComponent

struct exampleStruct {
    exampleStruct(/* args */);
    void exampleFunc(/* args */) {
        /* func code containing <GLFW/glfw3.h> items */
    }
}

#endif

/* component.cpp */
#include "component.h"

exampleStruct::exampleStruct(/* args */) {
    /* constructor code containing <GLFW/glfw3.h> items */
}

void exampleStruct::exampleFunc(/* args */) {
    /* func code containing <GLFW/glfw3.h> items */
}

Whereas I had access to <GLFW/glfw3.h> definitions in the header file, I lose access to them in the cpp file, and the program fails to build.

I feel like I have a decent grasp on C++, but I am far from an expert, so please forgive me if I am missing an obvious solution. How can I avoid this problem and/or better organize my file structure?

Edit: Sorry for the delayed response, I've been able to figure out how to replicate the linking errors (LNK2005, LNK2001, and LNK2019) with a minimally reproducible program. This is a github page for it and here is a printout of the program contents:

/* main.cpp */
#include "Header2.h"

int main() {

    library::object obj;
    library::object1 obj1;
    library::object2 obj2;

    return 0;
}
/* Header.h */
#pragma once // tried with and without, no difference
#include <iostream>

namespace library {
    template<typename T>
    void print(T input);

    void test()
    {// definition in the header rather than cpp causes LNK2005, 
     // but I'm not sure why it does this.
        std::cout << "This is a test." << std::endl;
    }

    struct object {
        static inline int i = 1;
        object();
        ~object() = default;
        // if I place 'void test()' and it's definition here,
        // then I don't get LNK2005
    };
}
/* Header.cpp */
#include "Header.h"

library::object::object()
{
    std::cout << "I am object!" << std::endl;
}

template<typename T>
void library::print(T input)
{
    std::cout << "This is the input: " << input << std::endl;
}
/* Header1.h */
#include "Header.h"

namespace library {
    struct object1 {
        object1();
        ~object1() = default;
    };
}
/* Header1.cpp */
#include "Header1.h"
#include <iostream>

library::object1::object1()
{
    std::cout << "I am object1!\n";
    library::print(object::i);
}
/* Header2.h */
#include "Header1.h"

namespace library {
    struct object2 {
        object2();
        ~object2() = default;
    };
}
/* Header2.cpp */
#include "Header2.h"
// including "Header.h" did not fix LNK2001 or LNK2019
#include <iostream>

library::object2::object2()
{
    std::cout << "I am object2!\n";
    library::print(object::i);
}
  • 4
    You need to `#include ` in each file that uses it. – dbush Sep 01 '23 at 03:31
  • 3
    Every header should include the headers it needs, that's it. To understand what you're seeing right now, understand that headers are never processed on their own, only as a part of .cpp files, where `#include` is textually replaced with the header's content. – HolyBlackCat Sep 01 '23 at 03:32
  • "I would like to move a lot of the definitions from my various header files to their cpp files": why? – user207421 Sep 01 '23 at 04:24
  • 2
    Generally speaking, your aim is misguided. Avoiding header files makes it *necessary* to *manually copy* declarations/definitions from the library into *every* source file in your project that uses the library - and that is error prone (e.g. if the declarations/definitions change in the library, it is *easy* to forget to update every source file, which typically causes *undefined behaviour* and bugs that are hideously difficult to track down). A primary *purpose* of header files is mitigating such problems. – Peter Sep 01 '23 at 07:23

2 Answers2

1

In your cpp+h example, you are still defining exampleFunc in your header file, AND now in your .cpp file too. That is an ODR (One Definition Rule) violation. You need to change your header file to simply declare exampleFunc, just like you did with the exampleStruct constructor.

Also, since the .cpp file now uses things from <GLFW/glfw3.h>, it needs to #include that file. Unless the .h file also needs things from that file, then leave the #include there instead.

Try something more like this, depending on what args actually need:

component.h

#ifndef hComponent
#define hComponent

#include <GLFW/glfw3.h>

struct exampleStruct {
    exampleStruct(/* args */);
    void exampleFunc(/* args */);
};

#endif

component.cpp

#include "component.h"

exampleStruct::exampleStruct(/* args */) {
    /* constructor code containing <GLFW/glfw3.h> items */
}

void exampleStruct::exampleFunc(/* args */) {
    /* func code containing <GLFW/glfw3.h> items */
}

Alternatively...

component.h

#ifndef hComponent
#define hComponent

struct exampleStruct {
    exampleStruct(/* args */);
    void exampleFunc(/* args */);
};

#endif

component.cpp

#include "component.h"
#include <GLFW/glfw3.h>

exampleStruct::exampleStruct(/* args */) {
    /* constructor code containing <GLFW/glfw3.h> items */
}

void exampleStruct::exampleFunc(/* args */) {
    /* func code containing <GLFW/glfw3.h> items */
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Hey @RemyLebaeu, I tried following your implementation, but now I am just getting an atrocious amount of linking errors (LNK2019, LNK2005, and LNK2001). – ModernEraCaveman Sep 02 '23 at 00:06
  • 1
    @ModernEraCaveman Then you are doing other things wrong that we can't see. [LNK2001](https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/linker-tools-error-lnk2001) and [LNK2019](https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/linker-tools-error-lnk2019) occur if you refer to things in your code that the linker can't find in your code or referenced libraries. [LNK2005](https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/linker-tools-error-lnk2005) is the result of an ODR violation. – Remy Lebeau Sep 02 '23 at 00:26
  • @RemyLebaeu Thank you for your feedback. I just updated the post with a minimally reproducible program for the linking errors. Could you take a look at it please? – ModernEraCaveman Sep 02 '23 at 20:48
1

I might be wrong here, but the code you wrote definitely has some obvious mistakes. The main thing here is in this part:

/* vkGraphicsPipeline.h */
#include "vkInstance.h" // gains access to <GLFW/glfw3.h> within "vkInstance.h"

#include "component.h" // gets access to <GLFW/glfw3.h> through #include "vkInstance.h"
/* other headers similar to component.h */

the part where you told you'll get access to glfw on component.h/component.cpp which is included in vkInstance.h is the mistake . include chains .. doesnt work like that. theyre like.. series of header includes that passes down the includes that the included headers contains. you were supposed to include vkInstance.h on component.h, not on a separate file and expect the code to magically link the includes together. if that was true, then the whole header chain would be invalid.

try this and tell me if it doesn't work:

/* component.h */

#ifndef hComponent
#define hComponent

#include"vkInstance.h"
//this makes sure that vkInstance is included on component.h ,
//which makes it able to include everything that was included
//in vkInstance.h on component.h

struct exampleStruct {
   exampleStruct(/* args */);
   void exampleFunc(/* args */) {
       /* func code containing <GLFW/glfw3.h> items */
   }
}

#endif

/* component.cpp */
#include "component.h"

exampleStruct::exampleStruct(/* args */) {
   /* constructor code containing <GLFW/glfw3.h> items */
}

void exampleStruct::exampleFunc(/* args */) {
   /* func code containing <GLFW/glfw3.h> items */
}

you necessary don't need to include them again and again if you can manage the chains

and about that linker errors you'll get, you might need to try linking the lib file to the program. the glfw has a glfw3.lib file on somewhere between the folders of it. try linking it and see if it still gives the error

Good luck !!