1

Using C++14, I'm trying to define a variable in a namespace where commonly used variables are stored (App::Common). The main function should be the one that sets it, since it gets set to argv[0]. Meanwhile I need the variable to be visible by all other classes/files. But I get the linker error shown below. Also, ideally I would like the variable to be const where only the main function would set it once.

common.hpp

#pragma once
#include <string>
namespace App{
    namespace Common{
        extern std::string appPath;
    }
}

main.cpp

#include "common.hpp"
#include "client.hpp"
#include <string>
int main() {
    App::Common::appPath = argv[0];
}

client.hpp

#include "common.hpp"
class Client {
    public:
    void printAppPath();
};

client.cpp

#include <iostream>
#include <string>
#include "common.hpp"
#include "client.hpp"
void Client::printAppPath() {
    std::cout << App::Common::appPath << std::endl;
}

I get the following error by the linker:

ld: main.o: in function `main':
main.cpp:(.text.startup.main+0x25): undefined reference to `App::Common::appPath[abi:cxx11]'
ld: Client.o: in function `Client::printAppPath()':
Client.cpp:(.text...): undefined reference to `App::Common::appPath[abi:cxx11]'
A K
  • 737
  • 2
  • 8
  • 17
  • No, because in that post, there were no definitions. My problem is that there are definitions just either not visible, or considered multiple definitions. – A K Dec 27 '19 at 09:17
  • It is exactly same and all the same old boooring answers. – Öö Tiib Dec 27 '19 at 09:21
  • If it's the same, can you explain what changes can I do to my code. As I don't see it related. Thanks for your help. – A K Dec 27 '19 at 09:25
  • You have 2 answers answering it in exactly same way and you are arguing with those answers already so it is hard to imagine in where your confusion lies. – Öö Tiib Dec 27 '19 at 09:27

3 Answers3

2

This

#pragma once
#include <string>
namespace App{
    namespace Common{
        extern std::string appPath;
    }
}

contains only declaration of the variable appPath without its definition.

Here

#include "common.hpp"
#include "client.hpp"
#include <string>
int main() {
    App::Common::appPath = argv[0];
}

there is used the assignment operator to assign a value tp the variable appPath as if it were already defined. However actually its definition does not yet exist.

You can define the variable in any module in any enclosing namespace of the namespace Common or inside the namespace. For example you could define it in client.cpp like

std::string App::Common::appPth;
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • I do not want to define it in client.cpp. I want the main function to define it, and the client to use that definition. Anyway, I tried this in main.cpp: std::string App::Common::appPath{argv[0]}; But now I get: main.cpp: In function 'int main(int, const char**)': main.cpp: error: qualified-id in declaration before '{' token std::string App::Common::appPath{argv[0]}; – A K Dec 27 '19 at 09:23
  • 2
    @AK Either the variable is defined in a namespace or declared and defined in a block scope. You may not define a variable declared in a namespace in a block scope. – Vlad from Moscow Dec 27 '19 at 09:25
  • 1
    @AK You're confusing definition and assignment. – Holt Dec 27 '19 at 09:34
2

You are mixing definition and assignment, which are two different things for a variable:

  • a declaration for a variable x tells your compiler that there exists somewhere a variable named x;
  • a definition for a variable x tells your compiler that it needs to reserve some space for this variable x, and that the variable x will live at this location;
  • an assignment assigns a value to a variable.

For a variable, a declaration is usually a definition:

void foo() {
    int a; // Declaration AND Definition!
}

...except when the variable is marked as extern, since extern explicitly tells the compiler that this variable is defined elsewhere. In your case, this:

namespace App::Common {  // C++17 style
    extern std::string appPath;
}

...is a declaration, but this:

namespace App::Common {  // C++17 style
    std::string appPath;
}

...would be a definition (and also a declaration), and this:

int main(int argc, char *argv[]) {
    App::Common::appPath = std::string(argv[0]);
}

...is an assignment.

You should not define appPath in a header such as common.hpp, otherwize, you will have multiple definitions of the same variable (one for each .cpp file that includes your common.hpp) and the program will fail to compile.

What you want is a single definition for your program, and the only way to obtain it is to define App::Common::appPath once-and-for-all in a .cpp file. You can define it in main.cpp if you want:

#include <string>

#include "common.hpp"
#include "client.hpp"

// Definition:
std::string App::Common::appPath;

int main() {
    // Assignment:
    App::Common::appPath = argv[0];
}
Holt
  • 36,600
  • 7
  • 92
  • 139
1

You need definition:

in Common.cpp:

namespace App{
    namespace Common{
        std::string appPath;
    }
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • With definition, I get this error: ld: ./Client.o:(.bss._ZN4pkgr6common8pkgrPathB5cxx11E+0x0): multiple definition of App::Common::appPath[abi:cxx11]'; main.o:(.bss...): first defined here – A K Dec 27 '19 at 09:14
  • You need only one definition in a cpp, don't place it in several cpp. and don't put it in header neither. – Jarod42 Dec 27 '19 at 09:17
  • Yes, that's why in the OP code, I had extern. And only the main function did the definition. Can you elaborate? – A K Dec 27 '19 at 09:18
  • 1
    Your main doesn't **define** it, but only assign a value to it. – Jarod42 Dec 27 '19 at 09:20