I am having a problem with a large project on visual studio 2005 on which I have run out of ideas.
I can't even put a working code snippet because I don't know what's related, but I will try:
I needed to make each .cpp file in my project have its own ID number, and create an instance of an object (which is globally accessible) that knows that ID.
I followed the help on the accepted answer on this thread How to manage file unique IDs in c++
and made it work in a sandbox environment.
Adding files, giving them a unique #define FILEID (FileId::ID_FileName)
and then accessing their instance works fine on the sandbox.
Now comes the trouble -
I pasted the code that makes files know their IDS to the main project, and compiled.
So far so good.
Now, I added to one of the existing .cpp files in the project:
#include "ids.h"
#define FILEID File1 // The FileId corresponding to this file
#include "global.h"
Still compiles, links, all good.
Adding these lines to a (any) second .cpp file in the project
now gives link error:
in which:
name1: 1st file I added the lines to (alphabeticcaly)
name2: other unrelated filename (which can also be the 2nd file I added the lines to, but may as well be just some other file)
The error
in name2.obj : error LNK2005: "public static class Instance & __cdecl Manager<3>::getInstance(void)" (?getInstance#$Manager#$02##SAAAVInstance##XZ) already defined in name1.obj
Some times the error is only in the second file, and sometimes (between consecutive builds without changes) the error appears on every .cpp file in the folder.
Looking in the intermediate file (the preprocessor output) on the files to which I added the lines shows exactly one appearance of the
template <>
Instance &Manager<FILEID>::getInstance()
{
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
with the correct FileId::ID_FileName, which is a different name than that of the other file.
Still, the linker thinks the same FileId is used in more than one file.
On unrelated files (which also give the exact same error), there is no appearance of getInstance() at all. Apparently, there shouldn't be a reason for the linker to shout there.
I checked, and no .cpp files include each other somewhere in the project.
I am completely out of ideas as to what could cause this
and would appreciate any help.
EDIT 1
ids.h
enum FileId{
ID_file1ID=3,//just to see a non zero number in the debugger, which I do
ID_file2ID,
//and so on
FileIdSize
}
EDIT 2
When these errors start, the compiler starts to behave extremely unexpectedly.
Adding the line sdfsdfgasaedfahjk to any file STILL COMPILES AND PASSES.
it clearly states the file name to which the line has been added to compiles.
It clearly states it links to it.
It passes.
I now can't trust the compiler.
No idea what's going on.
You have 2 cpp files defining the FILEID to the same value 3.
As for a MCVE:
ids.h:
#pragma once
#define File1 3
#define File2 3 //<--same value on purpose
global.h
struct Instance
{
};
struct Factory
{
Instance getInstance(int FileID) { return Instance(); }
};
template <int ID>
struct Manager
{
Factory factory;
Instance& getInstance();
Factory& getTheFactory() { return factory; }
};
template <>
Instance& Manager<FILEID>::getInstance()
{
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
name1.cpp
#include "ids.h"
#define FILEID File1 // The FileId corresponding to this file
#include "global.h"
name2.cpp
#include "ids.h"
#define FILEID File2 // The FileId corresponding to this file
#include "global.h"
As this compiles there is a special implementation for Manager<3>::getInstance(void) created for both name1.cpp and name2.cpp.
You can't use the same value for FILEID in 2 different compilation units.
EDIT: Check values while compiling
Requires the preprocessor definition __BASE_FILE__="%(Filename)%(Extension)"
(Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions)
template <>
Instance& Manager<FILEID>::getInstance()
{
#define _STR(x) #x
#define STR(x) _STR(x)
#define CHECK_ID() __pragma(message("Initializing \"Instance& Manager<FILEID>::getInstance()\" with FILEID="STR(FILEID)" in "STR(__BASE_FILE__)))
CHECK_ID()
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
Example-Output:
1>------Build started : Project : Test_Call, Configuration : Debug Win32------
1> name1.cpp
1> Initializing "Instance& Manager<FILEID>::getInstance()" with FILEID = FileId::ID_file1ID in "name1.cpp"
1> name2.cpp
1> Initializing "Instance& Manager<FILEID>::getInstance()" with FILEID = FileId::ID_file2ID in "name2.cpp"
1> Test_Call.vcxproj-><Project>\Debug\Test_Call.exe
== == == == == Build: 1 succeeded, 0 failed, 0 up - to - date, 0 skipped == == == == ==
EDIT: Using FileId values as template parameter (MSVE)
id.h
#pragma once
enum FileId {
ID_file1ID = 3,//just to see a non zero number in the debugger, which I do
ID_file2ID,
//and so on
FileIdSize
};
global.h
#pragma once
#include "ids.h"
struct Instance
{
};
struct Factory
{
Instance getInstance(int FileID) { return Instance(); }
};
template <FileId ID>
struct Manager
{
static const FileId manager_id = ID;
static Factory& getTheFactory() { return m_factory; }
static Instance& getInstance()
{
static Instance theInstance = getTheFactory().getInstance(manager_id);
return theInstance;
}
private:
static Factory m_factory;
};
global.cpp
#include "global.h"
Factory Manager<FileId::ID_file1ID>::m_factory;
Factory Manager<FileId::ID_file2ID>::m_factory;
name1.cpp
#include "global.h"
void test1()
{
Instance& a = Manager<FileId::ID_file1ID>::getInstance();
}
name2.cpp
#include "global.h"
void test2()
{
Instance& a = Manager<FileId::ID_file2ID>::getInstance();
}
test.cpp
#include <iostream>
#include "global.h"
using namespace std;
int main(int argc, char** argv)
{
Instance& a = Manager<FileId::ID_file1ID>::getInstance();
Instance& b = Manager<FileId::ID_file2ID>::getInstance();
Instance& c = Manager<FileId::ID_file1ID>::getInstance();
Instance* aptr = &a;
Instance* bptr = &b;
Instance* cptr = &c;
printf("aptr==bptr -> %s\n", (aptr == bptr) ? "true" : "false"); //->false
printf("aptr==cptr -> %s\n", (aptr == cptr) ? "true" : "false"); //->true (both use the instance from ID_file1ID
printf("bptr==cptr -> %s\n", (bptr == cptr) ? "true" : "false"); //->false
}
This is not an answer, but may prove useful in finding out what is wrong.
The following code is essentially the same as the original answer, but with all complexity stripped away at the expensive of needing boilerplate code in various places.
idmanager.h
struct Instance {/*...*/};
Instance &getFile1Instance();
Instance &getFile2Instance();
// etc...
idmanager.cpp
Instance &getFile1Instance()
{
static Instance file1instance;
return file1instance;
}
Instance &getFile2Instance()
{
static Instance file2instance;
return file2instance;
}
// etc...
In each file, place at the start
#include "idmanager.h"
and you can get the static Instance of any file in the obvious way.
This is as simple as it can possibly get, so copying it into your project simply can't cause a problem.
If the above example worked, then try making it slightly closer to the original answer: move the definitions of the getFileXInstance functions into the files themselves, and delete idmanager.cpp.
idmanager.h
struct Instance {/*...*/};
Instance &getFile1Instance();
Instance &getFile2Instance();
// etc...
file1.cpp
#include "idmanager.h"
Instance &getFile1Instance()
{
static Instance file1instance;
return file1instance;
}
file2.cpp
// etc...
Clearly this just moves the code around between different .obj files, so should still work.
Now replace each getFileXInstance function with a struct with a single static member function, getInstance, as follows:
idmanager.h
struct Instance {/*...*/};
struct Manager1
{
static Instance &getInstance(); // defined in file1.cpp
};
struct Manager2
{
static Instance &getInstance(); // defined in file2.cpp
};
// etc...
file1.cpp
#include "idmanager.h"
Instance &Manager1::getInstance()
{
static Instance file1instance;
return file1instance;
}
file2.cpp
// etc...
The previous step allows us to reduce the amount of boilerplate code using templates:
idmanager.h
struct Instance {/*...*/};
template <int id>
struct Manager
{
static Instance &getInstance(); // each instantiation has its definition in a different cpp file
};
file1.cpp
#include "idmanager.h"
template <>
Instance &Manager<1>::getInstance()
{
static Instance file1instance;
return file1instance;
}
This is where linker errors are most likely to start appearing again, if they do at all.
More repetition can also be removed by putting the common code in a shared header globals.h, and communicating the preprocessor constant FILEID to it.
idmanager.h
struct Instance {/*...*/};
template <int id>
struct Manager
{
static Instance &getInstance(); // each instantiation has its definition in a different cpp file
};
file1.cpp
#include "idmanager.h"
#define FILEID 1
#include "globals.h"
globals.h
template <>
Instance &Manager<FILEID>::getInstance()
{
static Instance theInstance;
return theInstance;
}
This last example is now the same as the original answer, with a few differences (no factories, no enums, no getThisFileInstance()) which are irrelevant to the linker errors. Therefore (assuming the first example worked) you can identify which change broke the program, and that should help to diagnose the real problem.
(note: although your error is exactly that which would appear if multiple files shared the same id, from the comments I assume this is not the case.)
Related
For a project that I am working on, I need to mock certain classes for testing to test different behaviours of functions. For testing I use gtest. Because I am working on a game, the speed and efficiency of the code is of the essence. Because of this requirement I do not want to mock my classes by using virtual functions, but I want to mock my classes with templates, so the implementation of the classes will be defined at compile time and I do not lose performance at run time. Furthermore, because I want to have the least amount of code bloat in my other header/source files I want to split my files into headers and source files, so that some of the includes can be set in the source file. This approach however comes with a couple of problems.
Because the templated functions are defined in a source file, there will need to be an explicit definition of the classes in the source file. Otherwise these templated functions will throw an 'undefined external symbol' error at compile time. This would not be a problem if I did not have two different projects, one for the game and one for testing, as I can't make an explicit definition of a mock in the test project.
I have tried a couple of solutions, but all of them have drawbacks. I will try to demonstrate what I have done with the following piece of code: (I know and use GMock, but this is an easier example)
//Game project
//Foo.h
<template class Bar>
class Foo
{
public:
Bar bar;
bool ExampleFunction();
}
//Foo.cpp
#include "Foo.h"
<template class Bar>
bool Foo::ExampleFunction()
{
return bar.Func() > 10;
}
//Testing project
//BarMock.h
class BarMock
{
public:
int Func();
int value;
}
//BarMock.cpp
#include "BarMock.h"
Bar::Func()
{
return value;
}
//TestFoo.cpp
#include "Foo.h"
TEST(Tests, TestExample)
{
Foo<BarMock> mocked;
mocked.bar.value = 100;
ASSERT_TRUE(mocked.ExampleFunction());
}
Solution 1: Include cpp file in testing project
This is already error prone, as including a cpp file is usually a no go. But if I only include the cpp file ONCE somewhere in the testing project it will not give me the 'c function already defined' error. This in my opinion is not a solid solution (although it is the solution I am currently using), because if I do need a templated class in 2 locations of my testing project this will (almost) always give an error.
//TestFoo.cpp
#include "Foo.h"
#include "Foo.cpp" // error prone, but does compile
TEST(Tests, TestExample)
{
Foo<BarMock> mocked;
mocked.bar.value = 100;
ASSERT_TRUE(mocked.ExampleFunction());
}
Solution 2: Create definitions in header file
This is less error prone, but comes with some other drawbacks. Like I have stated before I want to keep the bloat to a minimum, but with this solution I will also include all of the headers of the Foo header (say I need in Foo and include foo somewhere, then in somewhere I will also have ).
//Game project
//Foo.h
<template class Bar>
class Foo
{
public:
Bar bar;
bool ExampleFunction()
{
return bar.Func() > 10;
}
}
//Foo.cpp removed
Solution 3: Create virtual functions for mocks
This is my least favourite option, but it should be mentioned. Like I have stated before, this comes with a runtime performance hit and I do not want to change most of my functions to virtual functions. But in this way you will not get errors.
//BarMock.h
class BarMock
{
public:
int Func() override;
int value;
}
//BarMock.cpp
#include "BarMock.h"
Bar::Func() override
{
return value;
}
Which one of these options is the best? Is there any method that I have missed? I would love to hear someone's opinion about this as I could not find a 'good' solution online.
A variation of solution #1 by renaming the files:
Foo.h
#pragma once // or/and header guards
<template class Bar>
class Foo
{
public:
Bar bar;
bool ExampleFunction();
};
Foo.inl (or other extension .inc, .ixx, ...)
#pragma once // or/and header guards
#include "Foo.h"
template <class Bar>
bool Foo<Bar>::ExampleFunction()
{
return bar.Func() > 10;
}
Foo.cpp
#include "Foo.h"
#include "Foo.inc"
#include "Bar.h"
// explicit instantiation
template <> struct Foo<Bar>;
FooTest.cpp
#include "Foo.h"
#include "Foo.inc"
#include "BarMock.h"
// Testing code...
I have a confusing issue using a static member variable as a default parameter. Since the same language construct works in a different place, it might be related to project (DLL) inter-dependencies. So please accept my apologies if my example is too complex, but I should draw the whole picture since I do not have any idea what is wrong.
I have a base class (representing kind of an error code)
ErrorBase.h
class ErrorBase
{
public:
typedef unsigned long ErrorCode;
/// here go the error codes. For reasons I do not want to explain, I cannot use an enumeration here.
static const ErrorCode ERROR_UNINITIALIZED;
static const ErrorCode ERROR_OK;
///...and so on
ErrorBase(ErrorCode theCode = ERROR_UNINITIALIZED);
};
...and in ErrorBase.cpp, I am assigning values to the codes...
const ErrorBase::ErrorCode ErrorBase::ERROR_UNINITIALIZED = 0xffffffff;
const ErrorBase::ErrorCode ErrorBase::ERROR_OK = 0x0;
//.. and so on...
ErrorBase is exported from a DLL which provides some general purpose classes to our project
Now I am deriving another error class for more specific errors which has additional attributes specific for the particular type of error. The class SpecificError is part of a different DLL which links to the general purpose DLL containing ErrorBase. I have not included the dllimport/dllexport shebang, but we are using this all over the place and it works in all cases. If you have doubts, I can edit my code example.
SpecificError.h
class SpecificError : public ErrorBase
{
public:
static const ErrorCode SPECIFIC_ERROR_UNINITIALIZED;
static const ErrorCode SPECIFIC_ERROR_SOMETHING_WENT_WRONG;
SpecificError(ErrorCode theCode = SPECIFIC_ERROR_UNINITIALIZED);
};
... and in the SpecificError.cpp I am defining these values:
const SpecificError::ErrorCode SpecificError::SPECIFIC_ERROR_UNINITIALIZED = ErrorBase::ERROR_UNINITIALIZED;
Like ErrorBase, SpecificError is exported from the DLL handling specific functionality. Note that both error classes declare a constructor using the "UNINITIALIZED" value as a default for the error code.
Now I have a program being dependent on both DLLs, thus linking to both of them through the corresponding import libraries. This program includes ErrorBase.h and SpecificError.h. It does not seem to have any problems with ErrorCode.h, but about SpecificError.h I am receiving an
LNK2001 unresolved external symbol SpecificError::ErrorCode SpecificError::SPECIFIC_ERROR_UNINITIALIZED referenced in main.obj.
(remark: main.cpp does not explicitly use SpecificError, it just includes the header file).
I was able to work-around the problem by removing the default parameter from the SpecificError constructor and declaring a default constructor which in its implementation calls the inherited constructor of ErrorBase passing SPECIFIC_ERROR_UNINITIALIZED to it. This leads me to the assumption that the symbol SPECIFIC_ERROR_UNINITIALIZED is properly declared and defined but cannot be used as a parameter default. However, this seems to apply to SpecificError only, everything seems fine in ErrorBase.
Toolset: I am using Visual C++ 2017 as a compiler.
I recreated the linked error. Make the following changes to your files and it should work just fine based on the code snippets that you showed above:
SpecificError.cpp
// I sent theCode to the Base class
SpecificError::SpecificError(ErrorCode theCode) : ErrorBase(theCode)
{
// ...
}
In ErrorBase.cpp I just added the constructor but you probably already have this:
ErrorBase::ErrorBase(ErrorCode theCode)
{
// ...
}
After I did this, I had to also move the initializations of the static consts to the .h from the .cpp files. Then I tested the code by doing:
SpecificError e; // theCode ends up being 0xffffffff
SpecificError e1(20); // theCode ends up being 20
I hope that this helps you.
Here is what my ErrorBase.cpp looks like:
#pragma once
#include"ErrorBase.h"
#include<iostream>
ErrorBase::ErrorBase(ErrorCode theCode) {
std::cout << theCode << std::endl;
}
ErrorBase.h:
#pragma once
class ErrorBase
{
public:
typedef unsigned long ErrorCode;
static const ErrorCode ERROR_UNINITIALIZED = 0xffffffff;
static const ErrorCode ERROR_OK = 0x0;
ErrorBase(const ErrorCode = ERROR_UNINITIALIZED);
};
SpecificError.cpp:
#pragma once
#include"SpecificError.h"
SpecificError::SpecificError(ErrorCode theCode) : ErrorBase(theCode)
{
}
SpecificError.h:
#pragma once
#include "ErrorBase.h"
class SpecificError : public ErrorBase
{
public:
static const ErrorCode SPECIFIC_ERROR_UNINITIALIZED = ErrorBase::ERROR_UNINITIALIZED;
static const ErrorCode SPECIFIC_ERROR_SOMETHING_WENT_WRONG = -42;
SpecificError(ErrorCode theCode = SPECIFIC_ERROR_UNINITIALIZED);
};
I tried this and it is working, the class name was missing in ErrorBase.cpp
const ErrorBase::ErrorCode ErrorBase::ERROR_UNINITIALIZED = 0xffffffff;
const ErrorBase::ErrorCode ErrorBase::ERROR_OK = 0x0;
If still not working then let me know.
You doing it wrong. The linker error means that it doesn't know where to get your constant value. You should use dynamic linkage with a first DLL. Let me show
Example of C++ class export:
C++ How to export a static class member from a dll?
And your code should be changed:
ErrorBase.h
#ifndef MAIN_DLL
#define MAIN_DLL 1 // or you can add MAIN_DLL definition to the your first project Macroses
#endif
#if MAIN_DLL
#define ERROR_API __declspec(dllexport) // export things to other modules
#else
#define ERROR_API __declspec(dllimport) // import things from the external DLL
#endif
class ERROR_API ErrorBase
{
public:
typedef unsigned long ErrorCode;
/// here go the error codes. For reasons I do not want to explain, I cannot use an enumeration here.
static const ErrorCode ERROR_UNINITIALIZED;
static const ErrorCode ERROR_OK;
///...and so on
ErrorBase(ErrorCode theCode = ERROR_UNINITIALIZED);
};
SpecificError.h
#pragma once
#define MAIN_DLL 0
#include "../Dll_stack_ovfl1/ErrorBase.h" // change it to your path
class SpecificError : public ErrorBase
{
public:
static const ErrorCode SPECIFIC_ERROR_UNINITIALIZED;
static const ErrorCode SPECIFIC_ERROR_SOMETHING_WENT_WRONG;
SpecificError(ErrorCode theCode = SPECIFIC_ERROR_UNINITIALIZED);
};
And the final step, configure the second DLL project to link it with exports of the first one:
Configuration Properties/Linker/Input/Additional dependencies
Add something like "$(SolutionDir)$(Configuration)\ErrorBase.lib"
Once again its just example, I don't know the real path of "lib" file and your project name for ErrorBase DLL - change it to your specific.
Consider the following:
// defs.h
extern "C" {
typedef struct t_state
{
bool b;
} t_state;
static t_state _state;
void t_state_allocate();
}
// defs.cpp
#include "defs.h"
void t_state_allocate()
{
_state.b = true;
printf("G state at %p is %d\n", &_state, _state.b);
}
// App.hpp
#include "defs.h"
class App
{
App();
};
// App.cpp
#include "App.hpp"
App::App()
{
printf("1 state at %p is %d\n", &_state, _state.b)
t_state_allocate();
printf("2 state at %p is %d\n", &_state, _state.b)
}
Output under G++ is something like:
1 state at 0x736e20 is 0
G state at 0x9cb140 is 1
2 state at 0x736e20 is 0
Here the expected behaviour is to access the same structure.. Where's the mistake ?
EDIT 1
t_state must be a pure C struct because it is used in other .c source code (and thus the extern keyword). However I can grant that nobody modifies its content.
In your code, you have two different global variables: one at App.cpp including defs.h, and the other one at defs.cpp including defs.h.
You have to use extern as you can see here:
How do I use extern to share variables between source files?
defs.h is included twice in two different .cpp files. so there are two instances of _state getting created. This is because #include essentially pastes the contents of the header into the file that includes it. So how do you get around this problem? Use extern:
typedef struct t_state
{
bool b;
} t_state;
extern t_state _state; //Tells the compiler to share the variable between files.
Now you can use the variable in each source file:
#include "defs.h"
void t_state_allocate()
{
_state.b = true;
printf("G state at %p is %d\n", &_state, _state.b);
}
c++ (not c++11)
Say i have 100 .cpp files in my project, which currently do some work.
All of those files currently include some globals.h file which i can edit easily.
I want each of those files to have its own instance of some object, and I also want that instance to have some unique ID of the file in which it is instantiated.
In addition, I want those objects to be created by a factory method, and I need the instances to have some way for a user to handle them - meaning they can't be anonymous.
In short - I need a way to generate unique IDs for all of the files in my project, i need the file to be able to access its own ID using the same name in all files, but also to be able to access the ID of a file externally in another "manager" file.
Here are options that don't work:
1.
Enum:
If I use an enum and give every file an enum ID, now I can't do this in globals.h:
static thePrivateInstanceInThisFile = theFactory.makeInstance(fileID);
because I need a different fileID in every file, and that was defined statically, and uniquely named using my enum.
2.
Class that counts its own instances
Define in globals.h:
class FileIDGiver{
private:
static int currentID;//initialize to 0 in cpp
int myID;
public:
FileIDGiver(){
myID = currentID++;
}
int getFileID(){
return myID;
}
}
static FileIDGiver theFileId;
static thePrivateInstanceInThisFile = theFactory.makeInstance(theFileId.getFileID());
This will give an ID to each static file instace which is unique to the file, but now it is not manageable externally to the file.
I thought about doing something like
globals.cpp
int file1ID;
int file2ID;
...
globals.h
extern file1ID;
extern file2ID;
...
file1.cpp
file1ID = theFileId.getFileID();
file2.cpp
file2ID = theFileId.getFileID();
...
and whenever a user needs to manage a file he would either use the file's ID variable, or create a new one in the above manner.
This would allow me to access each uniquely and automatically file ID externally.
The only problem I have with this is the line file1ID = theFileId.getFileID(); only executes in runtime, AFTER the line static thePrivateInstanceInThisFile = theFactory.makeInstance(theFileId.getFileID());.
which executes at compile time.
I can't figure out a good way to reverse this order, or maybe do a whole other mechanic.
Again - I need:
Automatically created file IDs
Unique file IDs (which are very very preferably numbers)
Usage of those IDs by the same variable name in all files (automatically, using a static variable definition in the globals.h file)
Ability to access a specific file ID manually by using another manually defined variable.
Please advise some good way to accomplish this
Thanks.
This sounds like a bad case of the static initialization order fiasco.
Here is a solution which uniquely assigns integer ids to each file, then generates a unique Instance by calling a factory function with the file's id, while ensuring that the Instance factory is initialized before its first use:
idgiver.h:
class IdGiver
{
int id;
public:
IdGiver() : id(0) {}
int getId() {return id++;}
};
IdGiver &getTheIdGiver();
idgiver.cpp:
#include "idgiver.h"
IdGiver &getTheIdGiver()
{
static IdGiver theIdGiver;
return theIdGiver;
}
factory.h:
class Instance
{
// ...
};
class Factory
{
// ...
public:
Factory() : {/*...*/}
Instance getInstance(int id) {/*...*/}
};
Factory &getTheFactory();
factory.cpp:
#include "factory.h"
Factory &getTheFactory()
{
static Factory theFactory;
return theFactory;
}
globals.h:
#include "idgiver.h"
#include "factory.h"
static int thisFileId = getTheIdGiver().getId();
static Instance thisFileInstance = getTheFactory().getInstance(thisFileId);
If you want to be able to access the static Instances of other files, then this cannot be done via an automatically generated id because the id generated for a file could change each time a new file is added, or every time it is compiled, or even on each execution. Therefore in this solution, each file manually defines its own persistent id similarly to example 1 in the question.
ids.h
enum FileId
{
File1, File2, File3
};
factory.h
#include "ids.h"
#include "instance.h"
class Factory
{
// ...
public:
Factory() {/*...*/}
Instance createInstance(FileId fileid) {/*...*/}
};
Factory &getTheFactory();
factory.cpp
#include "factory.h"
Factory &getTheFactory()
{
static Factory theFactory;
return theFactory;
}
idmanager.h
#include "ids.h"
#include "instance.h"
template<FileId id>
struct Manager
{
static Instance &getInstance(); // not defined
};
global.h
#include "idmanager.h"
#include "factory.h"
template <>
Instance &Manager<FILEID>::getInstance()
{
static Instance theInstance = getTheFactory().getInstance(FILEID);
return theInstance;
};
static Instance &getThisFileInstance()
{
return Manager<FILEID>::getInstance();
}
Usage is as follows: for each file requiring a static Instance object, place at the start
#define FILEID File1 // The FileId corresponding to this file
#include "global.h"
Then in any file,
The unique id is given by FILEID. (sorry it's a macro)
The static Instance of this file is obtained by getThisFileInstance().
The static Instance of any file is obtained by Manager<any_file_id>::getInstance().
This works by placing the implementation for an instantiation of the template Manager<FileId> in each file, each of which creates and returns that file's static Instance.
Advantages are persistence of ids, and zero run-time overhead: no need to dynamically assign ids, and the calls to Manager<file_id>::getInstance() are resolved at compile-time.
Also, the ids.h header can easily be generated by a script which scans the first line of each file for #define FILEID fileid, so the only maintenance left is remembering to write #define FILEID fileid.
You might modify your building procedure (e.g. your Makefile) to define some unique thing. E.g. you could compile your foo23.cpp file with something like (assuming GCC on some Linux system; adapt this to your compiler and OS and builder)
g++ -Wall -c -DBASENAME="$(basename foo23.cpp)" -DUNIQUEID=23
You could get the 23 for UNIQUEID using some shell script or whatever, e.g. an ad-hoc rule in your Makefile. Details depend upon your file naming conventions.
then use appropriately BASENAME and UNIQUEID in your C or C++ code (perhaps with dirty #if UNIQUEID==23 preprocessor tricks...).
So the idea is to generate the UNIQUEID in your build system and to pass it thru some preprocessor symbols. Details are OS, compiler, build-system specific.
You might also do some crude meta-programming by generating some C or C++ source or header file (perhaps using some awk script or some GPP or m4 preprocessing) in your building procedure.
I have a solution set up with 3 projects:
Lib1 (A static library)
Dll1 (A dynamic library)
App1 (A Blank Windows Store application, could be any application type)
Lib1 contains the following:
Export.h
#pragma once
#ifdef DLL_EXPORT
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
Lib1.h
#pragma once
#include "Export.h"
class Member { };
class EXPORT Base {
public:
static Member* GetStaticMember();
virtual void SetMember(Member* member) = 0;
protected:
static Member* m_member;
};
class Worker {
public:
void DoSomething();
};
Lib1.cpp
#include "pch.h"
#include "Lib1.h"
Member* Base::m_member;
Member* Base::GetStaticMember() {
return m_member;
}
void Worker::DoSomething() {
Member* test = Base::GetStaticMember(); // RETURNS 0
}
Dll1 contains the following
Dll1.h
#pragma once
#include "Lib1.h"
#include "Export.h"
class EXPORT ChildA : public Base {
public:
virtual void SetMember(Member* member) override;
};
Dll1.cpp
#include "pch.h"
#include "Dll1.h"
void ChildA::SetMember(Member* member) {
Base::m_member = member;
Member* test = Base::GetStaticMember(); // RETURNS CORRECT ADDRESS
}
App1 contains the following
Member* member = new Member();
ChildA* childa = new ChildA();
childa->SetMember(member); // Base::GetStaticMember() inside this RETURNS CORRECT ADDRESS
Worker* worker = new Worker();
worker->DoSomething(); // Base::GetStaticMember() inside this RETURNS 0
The Problem
When stepping through in the debugger, Base::GetStaticMember() returns the correct address to the member after it is set (but still while inside childa->SetMember(). Once OUTSIDE childa, Base::GetStaticMember() returns 0 instead. When inside worker->DoSomething(), Base::GetStaticMember() is also returning 0. Can anyone explain to me what the cause of this is and how to fix it so that accessing Base::GetStaticMember() either outside or inside methods of the Dll or Lib will return the correct address and not 0?
If you're using a static lib linked to multiple DLL's and/or your EXE, each will get its own static member variable. Think of the mechanics of how the *link phase of each of these happens and you'll see why.
You can turn your static vars into pointers referencing shared memory backed by a memory mapped file, but I warn you it gets tricky to manage. I've done it by maintaining a temp memory-map-file using the name of the class+member+pid as the mapping name (note: the pid was to allow multiple processes to run without stomping on each others shared mem). it worked surprisingly well.
You should link lib1 only with Dll1 or with App1, not both.
Now you have Base::_member defined both in your Dll1 and App1.
As you defined your Base class __dllexport/__dllimport, it uses Dll1 static member, but you din't declared Worker with any attributes, and it uses local static member.
I assume, you should specify attributes for Worker and do not link App1 with Lib1