Global Function Definition in Header File - How to Avoid Duplicated Symbol Linkage Error

Global function definition in header file - how to avoid duplicated symbol linkage error

Use the inline keyword.

inline std::ostream& operator<< (std::ostream& o, const error_code& e) {
return o << "[" << e.hi << "," << e.lo << "]";
}

Why does global variables in a header file cause link error?

Lines like

const char    *Bittorrent     =   "BitTorrent protocol";
const char eight_byte[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

define theses global variables no matter if that codes is in header file or ine a .c directly (#include is just textual insertion of the header's contents).
Instead you should have the definitions in exaclty one source file and change the header to provide an extern declaration instead:

extern const char *Bittorrent;
extern const char *eight_byte;

Then all your sources using thes variables can be compiled but the linker will the variables once only.

Duplicate Symbol when including header only library more than once

As alk already commented, you need to declare the functions and global variables in the header file static.

The static keyword means the object (function or variable) has internal linkage; that it is only visible in the current compilation unit, and it will not be included in any symbol tables.

It is also a good idea to use include guards, so that if you have another header file that includes your header, and the C source file includes both your header file and that other header file, your header file only gets included once. (The lines with FOOLIB_H comprise the include guard in the below example.)

Consider the following trivial fixed-size stack example, foolib.h:

#ifndef   FOOLIB_H
#define FOOLIB_H

#include <stdlib.h>

#define STACK_MAX 256

static size_t stack_size = 0;
static double stack_item[STACK_MAX];

static inline int stack_push(const double item)
{
if (stack_size < STACK_MAX) {
stack_item[stack_size++] = item;
return 0;
} else
return -1;
}

static inline double stack_pop(const double empty)
{
if (stack_size > 0)
return stack_item[--stack_size];
else
return empty;
}

#endif /* FOOLIB_H */

Each compilation unit (each source file you compile separately) that includes the above (#include "foolib.h"), gets their own local private stack they can use via stack_push() and stack_pop().

The reason for marking the functions static inline rather than just static is that the former tells the compiler that it is okay to omit the function altogether, if it is not used. In particular, if you compile code with gcc -Wall, gcc warns if a static function is not used, but does not warn if a static inline function is not used. Other than that, there is not much practical difference.

duplicate symbol of a function defined in a header file

If I make it inline, the linker errors go away. That's not something I want to rely on, since inline is a request not a guarantee.

It's OK to inline the function.

Even if the object code is not inlined, the language guarantees that is will not cause linker errors or undefined behavior as long as the function is somehow not altered in different translation units.

If you #include the .hpp in hundreds of .cpp files, you may notice a bit of code bloat but the program is still correct.

What causes this behavior? I'm not dealing with a scenario where I'm returning some kind of singleton.

The #include mechanism is a convenience for reducing the amount of code you have to manually create in multiple files with the exact content. In the end, all translation units that #include other files get the lines of code from the files they #include.

If you #include file_ops.hpp in, let's say, file1.cpp and file2.cpp, it's as if you have:

file1.cpp:

bool systemIsLittleEndian() {
uint16_t x = 0x0011;
uint8_t *half_x = (uint8_t *) &x;
if (*half_x == 0x11)
return true;
else
return false;
}

file2.cpp:

bool systemIsLittleEndian() {
uint16_t x = 0x0011;
uint8_t *half_x = (uint8_t *) &x;
if (*half_x == 0x11)
return true;
else
return false;
}

When you compile those two .cpp files and link them together to create an executable, the linker notices that there are two definitions of the function named systemIsLittleEndian. That's the source of the linker error.

One solution without using inline

One solution to your problem, without using inline, is:

  1. Declare the function in the .hpp file.
  2. Define it in the appropriate .cpp file..

file_ops.hpp:

bool systemIsLittleEndian(); // Just the declaration.

file_ops.cpp:

#include "file_ops.hpp"

// The definition.
bool systemIsLittleEndian() {
uint16_t x = 0x0011;
uint8_t *half_x = (uint8_t *) &x;
if (*half_x == 0x11)
return true;
else
return false;
}

Update

Regarding

 bool MY_LIB_EXPORT someFunc();// implemented in `file_ops.cpp`

There is lots of information on the web regarding. This is a Microsoft/Windows issue. Here are couple of starting points to learn about it.

  1. Exporting from a DLL Using __declspec(dllexport)
  2. Importing into an Application Using __declspec(dllimport)

Global objects in C++ says duplicate symbols

If you really need to declare global objects in header, then try this:

// Test.h
#ifndef TEST_H
#define TEST_H

#include <iostream>
using namespace std;

class Test {

public:
Test();
~Test();
void setName(string);
string getName();

private:
string name;
};
extern Test test1, test2; // This is what I called a `global object`

#endif

Now in the implementation file (Test.cpp), define them:

// Test.cpp
Test test1, test2;
// continue your implementation of the class Test as earlier

How to fix duplicated functions in object files?

In your wrapper.h

extern char key;

void printUI();
void RunApplication();
PersonalInfo CreatePersonalInfo(std::string &, std::string &, int & );
ContactInfo CreateContactInfo(std::string &, std::string &, std::string &, int&);
void DisplayAllCustomers();
void AddCustomerToList(Customer &);
Customer CreateCustomer(PersonalInfo &, ContactInfo &);

extern std::vector<Customer> customers;

And declare both in One and only one .cpp file.

If you got C++17 you can just exchange the extern with inline and your done.



Related Topics



Leave a reply



Submit