Sign in to follow this  
Sagar_Indurkhya

Confused about Multiple Definitions in C++ Programs

Recommended Posts

I've been searching around google about this one option I use when compiling my program with g++, "-z muldefs". I understand that this means I'm declaring things multiple times in my program. But I'm kinda confused about how to fix my program from these multiple definitions? Sorta how my program is: - A few .cpp files, but tons of .h files with the classes defined inline. - Lots of global variables accessed from throughout the program. - Quite a few places where there are circular references between classes (so I use preemptive class declarations). I know some of these are bad programming practice, and I'm looking to correct them. But I've always been a little confused about the whole "export" statement and such between source files.

Share this post


Link to post
Share on other sites
I would but it's around 10K lines and there isn't any particular file that's causing the problem (problem has metastasized you could say).

In any case, here is the makefile:


# NOTES:
# -g is for Compiling with Debug information for g++
# -z muldefs is required to allow for multiple definitions
# -o automates the compile-link process

########################### PLGRNSE MAIN ENGINE ###########################
# FOR DEBUGGING
#PLGRNSEmake: ./Source/main.cpp ./Source/Cell.cpp POPSPLITTERmake LATTICEGENmake
# g++ -g ./Source/main.cpp ./Source/Cell.cpp -z muldefs -o ./Build/PLGRNSE_BUILD

# OPTIMIZED
PLGRNSEmake: ./Source/main.cpp ./Source/Cell.cpp POPSPLITTERmake LATTICEGENmake
g++ -O3 ./Source/main.cpp ./Source/Cell.cpp -z muldefs -o ./Build/PLGRNSE_BUILD

########################### POPULATION SPLITTER ###########################
# FOR DEBUGGING
POPSPLITTERmake: ./Source/PopulationSplitter.cpp
g++ -g ./Source/PopulationSplitter.cpp -z muldefs -o ./Build/POPSPLITTER

# OPTIMIZED
#POPSPLITTERmake: ./Source/PopulationSplitter.cpp
# g++ -O3 ./Source/PopulationSplitter.cpp -z muldefs -o ./Build/POPSPLITTER

########################### LATTICE GENERATOR ###########################
# FOR DEBUGGING
LATTICEGENmake: ./Source/LatticeGenerator.cpp
g++ -g ./Source/LatticeGenerator.cpp -z muldefs -o ./Build/LATTICE_GENERATOR

# OPTIMIZED
#LATTICEGENmake: ./Source/LatticeGenerator.cpp
# g++ -O3 ./Source/LatticeGenerator.cpp -z muldefs -o ./Build/LATTICE_GENERATOR



and Eclipse gives me the following error:

make PLGRNSEmake
g++ -g ./Source/PopulationSplitter.cpp -z muldefs -o ./Build/POPSPLITTER
g++.exe: muldefs: No such file or directory

but this is on Windows. When I'm doing this in Ubuntu, there is no such error. I think it's because MinGW/Cygwin's support for GCC is not as extensive?

Share this post


Link to post
Share on other sites
Ok I did all of that, but now one question I have is about enumerations. I have three files:

globals.cpp: declares enum SPECIES{blah blah blah}
globals.h: extern enum SPECIES;
main.cpp: tries to use constants in SPECIES.

But main.cpp is not recognizing enum SPECIES even though I have included globals.h.

G++ says that I cannot apply extern to an enumeration. How then can I make this enumeration globally visible?

Share this post


Link to post
Share on other sites
Quote:
Original post by Sagar_Indurkhya
Ok I did all of that, but now one question I have is about enumerations. I have three files:

globals.cpp: declares enum SPECIES{blah blah blah}
globals.h: extern enum SPECIES;
main.cpp: tries to use constants in SPECIES.

But main.cpp is not recognizing enum SPECIES even though I have included globals.h.

G++ says that I cannot apply extern to an enumeration. How then can I make this enumeration globally visible?


Enums define data types, not storage, so it is legit to put them in the header, just like function prototypes and class bodies.

Share this post


Link to post
Share on other sites
Ah I thought about that. But see, the program is designed around other people modifying one specific source file (globals.cpp) and only that file if I can help it. The enumeration defined in globals.cpp is very important specifically for that purpose, which is why initially all that stuff was in globals.h, and then I had to use -z muldefs. Is there no way to make it visible from the source file instead of declaring it in the header file?

Share this post


Link to post
Share on other sites
Quote:
Original post by Sagar_Indurkhya
Ah I thought about that. But see, the program is designed around other people modifying one specific source file (globals.cpp) and only that file if I can help it. The enumeration defined in globals.cpp is very important specifically for that purpose, which is why initially all that stuff was in globals.h, and then I had to use -z muldefs. Is there no way to make it visible from the source file instead of declaring it in the header file?


sup Sagar, I'm Carl and I was in your multi-var class at NCSSM.

but why would you want to declare the enum in the source file if you want it to be globally visible? an enum is a data type and therefore should technically be in a header.

have you tried using header guards? they can help you out with multiple definitions.

example:

#ifndef HEADER_FILE_NAME_H
#define HEADER_FILE_NAME_H

// code

#endif // HEADER_FILE_NAME_H



the end result is that if a header has already been included, it won't be included a second time.

also, I think it's good to avoid using things like global variables left over from the days of C, and instead use proper object-oriented programming practices to achieve the same effect.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sagar_Indurkhya
But see, the program is designed around other people modifying one specific source file (globals.cpp) and only that file if I can help it.


What's in globals.cpp that people are modifying. If it's the values of variables that stuff should be in a text file that you parse at application startup, not in code.

-me

Share this post


Link to post
Share on other sites
Quote:
Original post by Sagar_Indurkhya
Ah I thought about that. But see, the program is designed around other people modifying one specific source file (globals.cpp) and only that file if I can help it.


"modifying" how? Have you considered the possibility that it might not be necessary to recompile for these changes (preferring a scripting or data-driven solution instead)?

Share this post


Link to post
Share on other sites
It's a scientific program for simulating stuff (www.sourceforge.net/projects/plgrnse). The idea is that to make a simulation really flexible, you need to be able to modify how the innards work. I know there is always the saying that you should seperate your programmer from your user, but in this case, the scientists often have extensive programming backgrounds (at least those for whom this program is aimed at), and also I have spent alot of time developing walkthroughs/documentation/tutorials for the program. The program is pretty small, 3K~ish lines of code, and recompiling takes very little time (compared to the run time of the program, which can run from hours to days).

The way the enumeration is used is:


// Store the molecules in an enumeration so you can use the names
// when defining the GLOBAL_REACTION_LIST. Example, if you had the following
// types of molecules: X, A, B, and Z, you would modify the line
// below to look something like:
//
// enum SPECIES {MOL_X, MOL_A, MOL_B, MOL_Z};

enum SPECIES
{
// Promoters
Plac, Plac_lacI, Plac_lacI_lacI,
PcI, PcI_cI, PcI_cI_cI,
Ptet, Ptet_tetR, Ptet_tetR_tetR,
PtetGFP, PtetGFP_tetR, PtetGFP_tetR_tetR,

// mRNA
mRNA_lacI, mRNA_cI, mRNA_tetR, mRNA_GFP,

// Proteins
Protein_tetR, Protein_lacI, Protein_cI, Protein_GFP,

// IPTG Related
#ifdef IPTG_100uM
IPTG_CHEMICAL, Plac_IPTG,
#endif

NULL_SPECIES // Used primarily for degradation
};

...

void initialize()
{
...

AddDegradationReaction(Protein_tetR, protein_degradation_rate);
AddDegradationReaction(Protein_lacI, protein_degradation_rate);

...

AddHeterodimerBindingReaction(Plac, Protein_lacI, Plac_lacI, P_binding);
AddHeterodimerBindingReaction(Plac_lacI, Protein_lacI, Plac_lacI_lacI, P_binding);

...

}




At this point I'm thinking I might try using a struct or class declaration.

Share this post


Link to post
Share on other sites
Quote:
Original post by Aressera
Quote:
Original post by Sagar_Indurkhya
Ah I thought about that. But see, the program is designed around other people modifying one specific source file (globals.cpp) and only that file if I can help it. The enumeration defined in globals.cpp is very important specifically for that purpose, which is why initially all that stuff was in globals.h, and then I had to use -z muldefs. Is there no way to make it visible from the source file instead of declaring it in the header file?


sup Sagar, I'm Carl and I was in your multi-var class at NCSSM.

but why would you want to declare the enum in the source file if you want it to be globally visible? an enum is a data type and therefore should technically be in a header.

have you tried using header guards? they can help you out with multiple definitions.

example:

#ifndef HEADER_FILE_NAME_H
#define HEADER_FILE_NAME_H

// code

#endif // HEADER_FILE_NAME_H



the end result is that if a header has already been included, it won't be included a second time.

also, I think it's good to avoid using things like global variables left over from the days of C, and instead use proper object-oriented programming practices to achieve the same effect.


Hey Carl! Yeah I put in include guards. The problem I'm facing with OOP (which I do use to a certain degree in the project) is that the program has to be fast. Of course OOP can be fast, but there is also the saying that not everything has to be written with an OOP approach. The program is simple in how the whole thing is structured, but very complicated (and easily tends itself to become convoluted) algorithmically. Also speed is of the absolute essence as this thing literally takes days just for one simulation run.

Finally, one problem with OOP is that it limits freedom in a sense of how a programmer can extend it. Sure, I may not see any need to access some global array that's used for output from the middle of the Cell class. But someone else might. So some things (like system wide parameters, things that are used by almost every part of the program anyways) are declared at the global scope.

___________________________

EDIT: I think I figured one sneak around way to do it:


// globals.cpp

class SPECIES
{
...
enum {blah, blah, blah}
};




// globals.h
class SPECIES;



I don't have access to a compiler right now, but I think that should work.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sagar_Indurkhya
EDIT: I think I figured one sneak around way to do it:

*** Source Snippet Removed ***

*** Source Snippet Removed ***

I don't have access to a compiler right now, but I think that should work.


No. Just... no.

In order for the enumeration constants to be visible in main.cpp, they must be defined within the main.cpp translation unit. In other words, the enumeration MUST be defined in the header; there is no way around that.

EDIT: Technically, you could #include the .cpp file, but that way lies madness, so don't.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anthony Serrano
Quote:
Original post by Sagar_Indurkhya
EDIT: I think I figured one sneak around way to do it:

*** Source Snippet Removed ***

*** Source Snippet Removed ***

I don't have access to a compiler right now, but I think that should work.


No. Just... no.

In order for the enumeration constants to be visible in main.cpp, they must be defined within the main.cpp translation unit. In other words, the enumeration MUST be defined in the header; there is no way around that.

EDIT: Technically, you could #include the .cpp file, but that way lies madness, so don't.


Meh, in that case I'll just end up rerouting all parts of the program that need those variables to globals.cpp. There really isn't any other part of the program I can foresee where someone would need to access those constants (and if they did, it's highly likely they would understand the program well enough to know that those constants are just replacing integer values, and that most algorithms operate on all the species at once so no one species needs to be singled out).

Thanks for all the help, I've learned a couple new tricks from this thread.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sagar_Indurkhya
Meh, in that case I'll just end up rerouting all parts of the program that need those variables to globals.cpp. There really isn't any other part of the program I can foresee where someone would need to access those constants (and if they did, it's highly likely they would understand the program well enough to know that those constants are just replacing integer values, and that most algorithms operate on all the species at once so no one species needs to be singled out).


Please, for the love of god, don't. You'll basically be throwing out good coding practices in the service of a hare-brained design.

I'm not going to beat around the bush here. Your design is flawed. Deeply. If you have a lot of classes defined in header files, lots of globals, and lots of circular references, your design is fundamentally broken.

Rather than trying to coerce the compiler into doing what you want, you should spend your time learning proper software design first.

Trust me, if your end users are willing and pable to extend your code, they will much prefer working on several files that are coded properly rather than on one single file that is an enormous kludge.

In fact, a cursory examination of your CVS tells me that your code is in MAJOR meed of refactoring. Pro tip: the instant you type "using namespace std;" in your C++ code, you've already violated good coding practices. Second major tip: your globals.cpp doesn't do anything that can't be done easier (from the user's perspective) with configuration files.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this