Can I use a global variable in a library?

Hi guys!
I have this code:

File zts_tlib.h: library header:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef ZTS_TLIB_H
#define ZTS_TLIB_H

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

#ifdef __dll__
#define IMPEXP __declspec(dllexport)
#else 
#define IMPEXP __declspec(dllimport)
#endif

extern "C" _stdcall IMPEXP 
    void foo();

#endif 


File zts_tlib.cpp: library source:
1
2
3
4
5
6
7
8
9
10
11
12
13
#define __dll__
#include <zts_tlib.h>

string logname;

void _stdcall foo()
{
    ofstream debug ("dbg.txt");
    debug << "foo" << endl;
    logname = "log.txt";
    debug << "after logname" << endl;
    debug.close();
}


File zts_usetlib.cpp: source of program who calls the library function foo():
1
2
3
4
5
6
7
#include "zts_tlib.h"
using namespace std;

int main()
{
    foo();
}


Well, when I access the global variable logname the program ends (dbg.txt contains only "foo").
The original program is a dll which is called by a VB COM wrapper, which is instantiated by PHP when a web page is loaded: when I read or write the library global variable Apache crashes.
There's something I'm missing...
Any help?
Thanks in advance!
Hey there, Z24.

The one thing that stuck out to me was the lack of a return statement in your main function. Usually (as I'm sure you know) you would return 0 on success.
You don't *have* to return 0, usually it is implied (although it is still go practice to do so).

Well...I guess you could...you will have to go to your other .cpps and delcare the variable as an extern though.
To answer the actual question...

The library does not export the global variable, so you are not supposed to have access to it. (In other words, it is not really a 'global' variable, since only the library properly knows about it.)

Remember, stuff in .h files is for the users of the libary. Everything else belongs to the library only.

If you wrote the library yourself, you can explicitly export it in the .h file:
extern string logname;
After that you can use it in your main() or anywhere else...


Beware that you must compile the library and your program with the same compiler and STL implementation. If you, for example, compile your library with MSVC++ and then compile your program with Watcom C++, chances are that accessing that string variable will cause problems.

Hope this helps.
Thanks everybody!

I don't export the global variable logname because I need to use it in many functions of my library but I don't need it in the main program. Or do I need to export it anyway because it's a global?

The actual purpose is this: I have a string logname and a ofstream zlog, which I use for debugging; the exported function "foo" calls other internal functions which call other functions, and so on.
I would like to be able to do zlog << something in every function and I would open the stream at the beginning of foo() and close it at the end of foo(), hence the two variables should be 'global' for the library functions only. Is that possible?

The library is mine and I compiled library and program with the free Borland 5.5.

It is not suggested to use a global variable, because the order of the initialization of these variables in different cpp files are undefined. You can use a static local variable instead. for example:
1
2
3
4
5
string& logname()
{
  static string name = "something";
  return name;
}

You just type a pair of brackets more when you need to access it.
All right. That's good, thanks.

But what about a global ofstream?
1
2
3
4
5
6
7
void zlog(stringstream& s)
{
    ofstream zl;
    zl.open(logname().c_str());
    zl << s.str();
    zl.close();
}


I should change every
zlog << "something " << 99 << endl;
to
stringstream ss; ss << "something " << 99 << endl; zlog(ss);

And by the way, I'd like to understand why the code I posted before does not work...
On second look, am I correct in thinking that you want to change the file that the debug stream writes to?

It doesn't really work that way. You must first close the first file, then open the new one. You can encapsulate this behavior in a class if you like:
1
2
3
4
5
6
7
8
9
10
struct debugstream: public ofstream
{
  void open(
    const char * filename,
    ios_base::openmode mode = ios_base::out
    ) {
    if (is_open()) close();
    open( filename, mode | ios_base::ate );
    }
};

Now you can use it thus:
1
2
3
4
5
  debugstream debug("dbg.txt");
  debug << "this line goes to 'dbg.txt'\n" << endl;
  debug.open("log.txt");
  debug << "this line goes to 'log.txt'\n" << endl;
  debug.close();

If this isn't what you wanted please explain.

The first code didn't work because you tried to access an invalid object (that is, one you aren't supposed to be able to see).
No, the file is one, and it's opened each time the library is called and closed before returning to the main program.
In the example I made logname as global and the ofstream as local, just because I found that accessing a global variable the program ends, but they're both global.
My problem is that the ofstream must be visible to all functions inside the library and must be managed entirely by the library itself. I don't want to pass the ofstream as an argument to every internal (not exported) function.

The first code didn't work because you tried to access an invalid object (that is, one you aren't supposed to be able to see).


I don't understand this: why shouldn't I be able to see logname if it's a global variable?
Last edited on
There is scope and visibility. The two aren't necessarily synonymous.

Scope is how accessible an object is according to syntax. The variable is technically a global variable (well, it probably is, depending on your compiler), hence it has global scope.

Visibility is where an object can be seen. Though the variable is global, it is not visible outside of the .cpp file, because it is only declared within that file's compilation. Compiling another file is another file -- there is no reason why that other file should have any idea what is in this file.


Hence, if you want the variable to be global, you must explicitly list it as extern in the .h file.

peekaboo.hpp
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef PEEKABOO_HPP
#define PEEKABOO_HPP

#include <string>

// This is the type and name of a variable defined elsewhere (in peekaboo.cpp)
extern std::string I_am_visible;

// This is the type and name of a function defined elsewhere (in peekaboo.cpp)
void print_I_am_invisible();

#endif 


peekaboo.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;

#include "peekaboo.hpp"

// Here is the definition of that string
string I_am_visible = "Hello world!";

// Here is a string that, while in global scope, is local to this .cpp file only
string I_am_invisible = "WoooOOOOooo!";

// Here is that function
void print_I_am_invisible()
  {
  cout << I_am_invisible << endl;
  }


main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;

// Include the file that tells us all about the things we can see/use from peekaboo.cpp
#include "peekaboo.hpp"

int main()
  {
  // Use the things we can see
  cout << I_am_visible << endl;
  print_I_am_invisible();
  return 0;
  }


Hope this helps.
Great explanation and example, thanks Duoas.

However, I fail to see what's wrong in my code: the declaration string logname is in the library .cpp and is used in the library .cpp, so its scope and its visibility should be the entire .cpp.
If I move it to the .h making it external I get the same behaviour: the program ends when I access it in the foo() function.
Maybe I'm missing what's the scope of a variable declared outside functions in a .cpp of a library.

And I've just realized I was not very clear: I first make the static library compiling the zts_tlib.h and zts_tlib.cpp, then I make the .exe program compiling the zts_usetlib.cpp and linking it to the previously generated .lib.
Last edited on
Topic archived. No new replies allowed.