views:

338

answers:

6

I'd like to make program-wide data in a C++ program, without running into pesky LNK2005 errors when all the source files #includes this "global variable repository" file.

I have 2 ways to do it in C++, and I'm asking which way is better.

The easiest way to do it in C# is just public static members.

C#:

public static class DataContainer
{
    public static Object data1 ;
    public static Object data2 ;
}

In C++ you can do the same thing

C++ global data way#1:

class DataContainer
{
public:
    static Object data1 ;
    static Object data2 ;
} ;
Object DataContainer::data1 ;
Object DataContainer::data2 ;

However there's also extern

C++ global data way #2:

class DataContainer
{
public:
    Object data1 ;
    Object data2 ;
} ;
extern DataContainer * dataContainer ; // instantiate in .cpp file

In C++ which is better, or possibly another way which I haven't thought about?

The solution has to not cause LNK2005 "object already defined" errors.

+5  A: 

A better way would be to put your objects in a namespace.

// header
namespace global // or any appropriate name
{
   extern Object data1 ;
   extern Object data2 ;
}

// cpp
namespace global // or any appropriate name
{
   Object data1 ;
   Object data2 ;
}

As precised in commments: to do this, don't forget to use extern qualifier to the declarations (in the header file) to specify that there is only one instance (in the cpp file) of each object.

Klaim
y? this is global data, so by definition it really makes sense to me for it to be in the global namespace.
bobobobo
@bobobobo: But in way #1 you scope the data in a class even though it's just doing the job of a namespace.
Charles Bailey
@Bobobobo: You're getting confused because you can't have true global data in C#. In C# everything needs to be in a class, whereas in C++ you can have true globals. Therefore, you'd have `namespace DataContainer { Object data1; Object data2; }`. The data is still global but it's contained just as if it were in a class.
Billy ONeal
Because you're putting them in a class with no good reason. So putting them directly in REAL global space with just a namespace to avoid name clashes would be a better solution. The alternative is the Singleton pattern.
Klaim
BillyONeal> exactly
Klaim
Yes, "None of the above" and this answer.
Jon Reid
This answer completely misses the boat. Namespacing doesn't solve the problem of every file needing to #include this DataContainer header.
bobobobo
@bobobobo: I think you need to clarify question. If you want global access to data then everywhere that needs to access the data needs to know the type and location of that data. The easiest way to do this is to have a single header that contains the type definition(s) and name of the variables through which the data is accessed. Whether the object and types are part of the global namespace or not is an orthogonal issue.
Charles Bailey
@Charles Bailey: __Precisely__ why this answer is useless. Using extern does this form me, as well as the static variables solution.
bobobobo
http://meta.stackoverflow.com/questions/14514/more-downvote-power-to-op
bobobobo
@bobobobo: Err.. they'd have to include the header in any case. Do you expect the class-with-static-members is going to magically be known in your other files?
Billy ONeal
@bobobobo: I don't understand what your objection to a header file is. You could just put `data1` and `data2` in the global namespace and use `extern Object data1;` where you need to use them; but then you can still use namespaces `namespace glbl { extern Object data1; }` without a separate header file. Either way I'd still argue that having a single place where the type and name of you globals is defined (i.e. a common header file) would be better.
Charles Bailey
@Charles. I'm not objecting to a header file. Simply wrapping globals in a namespace however, doesn't solve the problem where more than one file including that header will cause LNK2005: already defined object linker errors. You're right the question needs clarifying, but I'm maintaining that this answer does not address the crux of the question. http://www.cplusplus.com/forum/general/13759/.
bobobobo
@bobobobo: If you use `extern` on an object declaration and don't provide an initializer then it is just a declaration and not a definition whether it is in a namespace or not so you shouldn't be getting multiply defined symbol errors at link time. You will get undefined object errors if you don't define it in one source file (i.e. define without extern or with an initializer - or both).
Charles Bailey
@Charles. Yeah man, I know. I use extern to solve this problem.. this question has two solutions to the problem, and I'm asking which is better.
bobobobo
In the header file, in a namespace, declare all objects with extern. Then in a matching source file, define them all. Essentially, you've made the class a namespace by replacing static with extern.
GMan
See this answer is wrong because he doesn't say `extern`.
bobobobo
I think that's another "problem" as it's an implementation detail. But ok then I'll add the precision if it completes the answer for you.
Klaim
Anyway, I tried answered on your question about the better way to set your global variables. It's well known to C++ developers that using a class with public static members is totally overkill and misleading in C++. It's valid only in object-oriented-only languages like java and C# because there is no other way to do this (as other already pointed) in C++ you have several way to do this but the namespace is just the cleaner, simplera and less misleading way.
Klaim
+3  A: 

Public static variables are just globals, and globals are bad. If an object depends on some data you want to make global, make a setter for this dependency and have some kind of “factory” class that will put your application together from small, independent parts, providing the global dependencies through the setters. Like this, in pseudocode:

class A:
    has dependency1 of type X;
    has dependency2 of type Y;

class Factory:
    has sharedX of type X;
    has sharedY of type Y;

    init:
        sharedX = createX;
        sharedY = createY;

    wireApplication:
        instanceOfA = createA;
        instanceOfA.setDependency1(sharedX);
        instanceOfA.setDependency2(sharedY);
        return instanceOfA;

This way the A’s dependencies are not hard-coded by accessing static variables. That has several advantages. First, they are more visible, as you don’t secretly pull information from other classes in the implementation file. Second, they are not set in stone, because you can actually create A without pulling X and Y with it. Third, you have precise control over the lifetime of X and Y.

See the excellent series of blog posts by Miško Hevery about singletons – you can start with Singletons are Pathological Liars and follow the links from there.

zoul
+3  A: 

If you absolutely have to have some global objects then the simplest way is to just to declare them extern in a header file included anywhere that needs access to them and define them in a single source file.

Your way #1 uses a class with only static members which means that it is essentially doing the job of a namespace so why not just use a namespace?

Way #2 aggregates both objects in a single class but if there is no true interdependency between the two objects there is no particular benefit to this.

I'd recommend putting the objects in a namespace prevent pollution of the global namespace with potentially common identifiers like data1,

// File: globaldata.h
#ifndef GLOBALDATA_H
#define GLOBALDATA_H

#include "Object.h" // Definition of Object might be here

namespace GlobalData
{
    extern Object data1;
    extern Obejct data2;
}

#endif GLOBALDATA_H

.

// File: globaldata.cc
#include globaldata.h

namespace GlobalData
{

    Object data1;
    Object data2;
}

Then you can access them in other places like this.

#include "globaldata.h"

// Does something with global data
void f()
{
    GlobalData::data1.doSomething();
}
Charles Bailey
+2  A: 

You are confused. Your problem ("LNK2005") is with linkage, not the source language level scoping. C# conflates these things (although I don't think that's necessarily a bad thing) much more so than C++ (but C++ does so as well, to some extent, e.g. the extern keyword). No solution to this problem will live entirely in the realm of the compiler and the preprocessor. The linker and how you build your program will come into play.

Both of your solutions are actually using extern (external linkage) to solve the problem, it is just implicit in the first case. Really, "program wide" data is pretty vague. Do you mean "process wide"? Or shared across all instances of a program? Based on the error message, I assume you're dealing with windows. Does your program have/use DLLs? Does this "program wide" data need to be, or should be shared across different dlls?

Logan Capaldo
+1  A: 

In a header file (I'll call it Data.h):

namespace Data
{
    extern Object data1;
    extern Object data2;
}

In any source files that need to know about it:

#include "Data.h"

For Data.cpp, you can write either

#include "Data.h"

Object Data::data1;
Object Data::data1;

or

#include "Data.h"

namespace Data
{
    Object data1;
    Object data2;
}
Jon Reid
+1  A: 

KISS.

DataContainer.h

#pragma once
namespace DataContainer {
    extern Object data1;
    extern Object data2;
} // namespace DataContainer

DataContainer.cpp

#include <DataContainer.h>
namespace DataContainer {
    Object data1;
    Object data2;
}// namespace DataContainer
Jive Dadson