C++ Question Compiler error, multiple defined functions that aren't.

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
I have a header file that contains functions and variables that are being shared across multiple vessels and sub classes. This file is called "AAPO_Common.h"

Recently I've started getting the following compiler error...

Code:
1>LM_Cockpit.obj : error LNK2005: "void __cdecl to_quaternion(union VECTOR3,float,float * const)" (?to_quaternion@@YAXTVECTOR3@@MQAM@Z) already defined in LM.obj

...for each variable and function in the file.

The problem is that the only place they are defined is in AAPO_Common.h, and I've have it guarded (pragma once).

I haven't touched any of my includes so why would this error be croping up now, and what is causing it?

Contents of AAPO_Common.h...

Code:
// ==============================================================
//						ORBITER MODULE:
//                  Part of the ORBITER SDK
//               Copyright (C) 2013 Greg Hlynka
//                    All rights reserved
//
// AAPO_Common.h
// Common values and functions for the Apollo Aplication Project for Orbiter
//
// Notes: This file exists
// ==============================================================

#pragma once
#define STRICT

// Orbiter SDK files
#include "orbitersdk.h"

// ==============================================================
// Physical Constants
// ==============================================================
const double	METERS_AU			= 149597870700;		// Length of 1 Astronomical unit in meters

const double	STEFANBOLTZMANN		= 5.67e-8;			// Stephan-Boltzman constant for radiance of black bodies
const double	REGNAULT			= 8.314462175;		// Regnault (Ideal) gas constant 

const double	PA_TO_PSI			= 0.000145037738;	// Conversion rate of Pascals to PSI
const double	PSI_TO_PA			= 6894.75729;		// Conversion rate of PSI to Pascals

const double	H2_MOLAR_MASS		= 2.0158 / 1000;	// H2 molar mass [kg] 2 grams
const double	H2_HEAT				= 14.20;			// Specific heat of H2 [kj/kg K]

const double	N2_MOLAR_MASS		= 28.01344 / 1000;	// N2 molar mass [kg] 28 grams
const double	N2_HEAT				= 1.039;			// Specific heat of N2 [kj/kg K]

const double	O2_MOLAR_MASS		= 31.99886 / 1000;	// O2 molar mass [kg] 32 grams
const double	O2_HEAT				= 0.919;			// Specific heat of O2 [kj/kg K]

const double	CO2_MOLAR_MASS		= 44.00964 / 1000;	// CO2 Molar mass [kg] 44 grams
const double	CO2_HEAT			= 0.037;			// Specific heat of CO2 [kj/kg K]

const double	H2O_MOLAR_MASS		= 18.01528 / 1000;	// Water Molar mass [kg] 18 grams
const double	H2O_HEAT			= 4.187;			// Specific heat of Water [kj/kg K]

// ==============================================================
// Enumerators
// ==============================================================
enum chutestate		{STOWED, DROGUE, REEFED, UENREEFING, DEPLOYED, JETTISONED};
enum switchstate	{DOWN, CENTER, UP};
enum doorstate		{CLOSED, OPEN, CLOSING, OPENING};

// ==============================================================
// Functions
// ==============================================================

// --------------------------------------------------------------
// Convert axis/angle to quaternion form, angle is in radians.
// --------------------------------------------------------------
void to_quaternion (VECTOR3 axis, float angle, float result[4])
{
	normalise (axis);

	result[0] = axis.x * sin (angle / 2.0);
	result[1] = axis.y * sin (angle / 2.0);
	result[2] = axis.z * sin (angle / 2.0);
	result[3] = cos(angle / 2.0);
}

// --------------------------------------------------------------
// Convert unit-length quaternion to axis / angle pair.
// --------------------------------------------------------------
void from_quaternion (float q[4], VECTOR3 axis, float *angle)
{
	*angle = acos (q[3]) * 2.0;

	axis.x = q[0] / sin (*angle / 2.0);
	axis.y = q[1] / sin (*angle / 2.0);
	axis.z = q[2] / sin (*angle / 2.0);
}

// --------------------------------------------------------------
// Multiply two quaternions together.
// --------------------------------------------------------------
void multiply_quaternions (float q1[4], float q2[4], float result[4])
{

	result[0] = (q2[3] * q1[0]) + (q2[0] * q1[3]) + (q2[1] * q1[2]) - (q2[2] * q1[1]);
	result[1] = (q2[3] * q1[1]) + (q2[1] * q1[3]) + (q2[2] * q1[0]) - (q2[0] * q1[2]);
	result[2] = (q2[3] * q1[2]) + (q2[2] * q1[3]) + (q2[0] * q1[1]) - (q2[1] * q1[0]);
	
	result[3] = (q2[3] * q1[3]) - (q2[0] * q1[0]) - (q2[1] * q1[1]) - (q2[2] * q1[2]);
}

// --------------------------------------------------------------
// To rotate a vector in 3d space, angle is in radians
// --------------------------------------------------------------
VECTOR3 RotateVector (const VECTOR3& input, double angle, const VECTOR3& rotationaxis) 
{
	// To rotate a vector in 3D space we'll need to build a matrix, these are the variables required to do so.
	MATRIX3 rMatrix;
	double c = cos(angle);
	double s = sin(angle);
	double t = 1.0 - c;
	double x = rotationaxis.x;
	double y = rotationaxis.y;
	double z = rotationaxis.z;

	// Build rotation matrix
	rMatrix.m11 = (t * x * x + c);
	rMatrix.m12 = (t * x * y - s * z);
	rMatrix.m13 = (t * x * z + s * y);
	rMatrix.m21 = (t * x * y + s * z);
	rMatrix.m22 = (t * y * y + c);
	rMatrix.m23 = (t * y * z - s * x);
	rMatrix.m31 = (t * x * z - s * y);
	rMatrix.m32 = (t * y * z + s * x);
	rMatrix.m33 = (t * z * z + c);

	// Perform Rotation
	VECTOR3 output = mul(rMatrix, input); // multiply the input vector by our rotation matrix to get our output vector
	return output; // Return rotated vector
} // End "RotateVector"

// --------------------------------------------------------------
// Spawn a new vessel object (adapted from the Atlantis' ET seperation routine)
// --------------------------------------------------------------
OBJHANDLE SpawnObject (VESSEL *parent, char* name, char* classname, VECTOR3 ofs, double rvel) 
{
	VESSELSTATUS vs;
	parent->GetStatus (vs);

	parent->Local2Rel (ofs, vs.rpos);
	vs.eng_main = vs.eng_hovr = 0.0;
	vs.status = 0;

	VECTOR3 vel = { ofs.x, ofs.y, ofs.z};	// seperation vector
	normalise (vel);
	parent->Local2Rel (ofs, vs.rpos);		

	VECTOR3 rofs;
	parent->GlobalRot (vel, rofs);
	vs.rvel += rofs * rvel;

	oapiCreateVessel (name, classname, vs);
	return oapiGetObjectByName (name);
}
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,537
Reaction score
2,272
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
Why did you not make this function inline? Also, you can have this function defined multiple times, by the same header file being used for multiple cpp files.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Why did you not make this function inline?

Is that better in some way?

Also, you can have this function defined multiple times, by the same header file being used for multiple cpp files.

I thought I had already guarded against that. If not, how do I do so?
 
Last edited:

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,537
Reaction score
2,272
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
Is that better in some way?

Code:
Also, you can have this function defined multiple times, by the same header file being used for multiple cpp files.
I thought I had guarded against that. If not how do I do so?

Only have either inline functions or have function headers in header file and define the function in a .cpp file.

#pragma once is only valid for the compiler, not for the linker phase. If multiple object files produced by the compiler contain the same function, the linker complains.

#pragma once would prevent that you have the same header file being included multiple times by the same .cpp file.
 

orb

O-F Administrator,
News Reporter
Joined
Oct 30, 2009
Messages
14,020
Reaction score
4
Points
0
Is that better in some way?
Inlining? It makes somehow faster by removing calls, but larger code.


I thought I had guarded against that. If not how do I do so?

You include the header in each cpp file separately. The guard is only to prevent including it multiple times in the same cpp.

You can declare the functions in the header file, but define them in (only) one of cpp files.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
so how do I make the functions inline or otherwise prevent them from causing the above error while still being able to share them across multiple vessel classes and subclasses?
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,537
Reaction score
2,272
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
Inlining? It makes somehow faster by removing calls, but larger code.

Yes, it is better for small functions. Also, the function is not copied 1:1 into the code, but optimized with the surrounding code. Still, you should only use it for small functions, like in this example.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
You include the header in each cpp file separately. The guard is only to prevent including it multiple times in the same cpp.

You can declare the functions in the header file, but define them in (only) one of cpp files.

So I should make an "AAPO_Common.cpp" to go with my header?
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,537
Reaction score
2,272
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
so how do I make the functions inline

Put the keyword "inline" in front of them.

or otherwise prevent them from causing the above error while still being able to share them across multiple vessel classes and subclasses?

Reduce the function to the declaration/function header in the .h file. The header is "return-value function-name(parameters);"

Define the function in a .cpp file. The compiler only needs the function header to know that this function exists, while the linker later adds the definition of the function.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Ok I shifted the function bodies to a seperate .cpp file but now I'm getting "unresolved external" errors.

Is there something else I need to now about defining functions outside of a class?

How do the OrbiterAPI functions not run into this same problem?

---------- Post added at 15:08 ---------- Previous post was at 15:06 ----------

Header...

Code:
// ==============================================================
//						ORBITER MODULE:
//                  Part of the ORBITER SDK
//               Copyright (C) 2013 Greg Hlynka
//                    All rights reserved
//
// AAPO_Common.h
// Common values and functions for the Apollo Aplication Project for Orbiter
//
// Notes: This file exists
// ==============================================================

#pragma once
#define STRICT

// Orbiter SDK files
#include "orbitersdk.h"

// ==============================================================
// Physical Constants
// ==============================================================
const double	METERS_AU			= 149597870700;		// Length of 1 Astronomical unit in meters

const double	STEFANBOLTZMANN		= 5.67e-8;			// Stephan-Boltzman constant for radiance of black bodies
const double	REGNAULT			= 8.314462175;		// Regnault (Ideal) gas constant 

const double	PA_TO_PSI			= 0.000145037738;	// Conversion rate of Pascals to PSI
const double	PSI_TO_PA			= 6894.75729;		// Conversion rate of PSI to Pascals

const double	H2_MOLAR_MASS		= 2.0158 / 1000;	// H2 molar mass [kg] 2 grams
const double	H2_HEAT				= 14.20;			// Specific heat of H2 [kj/kg K]

const double	N2_MOLAR_MASS		= 28.01344 / 1000;	// N2 molar mass [kg] 28 grams
const double	N2_HEAT				= 1.039;			// Specific heat of N2 [kj/kg K]

const double	O2_MOLAR_MASS		= 31.99886 / 1000;	// O2 molar mass [kg] 32 grams
const double	O2_HEAT				= 0.919;			// Specific heat of O2 [kj/kg K]

const double	CO2_MOLAR_MASS		= 44.00964 / 1000;	// CO2 Molar mass [kg] 44 grams
const double	CO2_HEAT			= 0.037;			// Specific heat of CO2 [kj/kg K]

const double	H2O_MOLAR_MASS		= 18.01528 / 1000;	// Water Molar mass [kg] 18 grams
const double	H2O_HEAT			= 4.187;			// Specific heat of Water [kj/kg K]

// ==============================================================
// Enumerators
// ==============================================================
enum chutestate		{STOWED, DROGUE, REEFED, UENREEFING, DEPLOYED, JETTISONED};
enum switchstate	{DOWN, CENTER, UP};
enum doorstate		{CLOSED, OPEN, CLOSING, OPENING};

// ==============================================================
// Shared Functions
// ==============================================================
void to_quaternion (VECTOR3 axis, float angle, float result[4]);								// Convert axis/angle to quaternion form, angle is in radians.
void from_quaternion (float q[4], VECTOR3 axis, float *angle);									// Convert unit-length quaternion to axis / angle pair.
void multiply_quaternions (float q1[4], float q2[4], float result[4]);							// Multiply two quaternions together.

VECTOR3 RotateVector (const VECTOR3& input, double angle, const VECTOR3& rotationaxis);			// Rotate a vector in 3d space, angle is in radians

OBJHANDLE SpawnObject (VESSEL *parent, char* name, char* classname, VECTOR3 ofs, double rvel);	// Spawn a new vessel object (adapted from the Atlantis' ET seperation routine)

Source...
Code:
// ==============================================================
//						ORBITER MODULE:
//                  Part of the ORBITER SDK
//               Copyright (C) 2013 Greg Hlynka
//                    All rights reserved
//
// AAPO_Common.h
// Common values and functions for the Apollo Aplication Project for Orbiter
//
// Notes: This file exists
// ==============================================================

// Orbiter SDK files
#include "AAPO_Common.h"

// ==============================================================
// Functions
// ==============================================================

// --------------------------------------------------------------
// Convert axis/angle to quaternion form, angle is in radians.
// --------------------------------------------------------------
void to_quaternion (VECTOR3 axis, float angle, float result[4])
{
	normalise (axis);

	result[0] = axis.x * sin (angle / 2.0);
	result[1] = axis.y * sin (angle / 2.0);
	result[2] = axis.z * sin (angle / 2.0);
	result[3] = cos(angle / 2.0);
}

// --------------------------------------------------------------
// Convert unit-length quaternion to axis / angle pair.
// --------------------------------------------------------------
void from_quaternion (float q[4], VECTOR3 axis, float *angle)
{
	*angle = acos (q[3]) * 2.0;

	axis.x = q[0] / sin (*angle / 2.0);
	axis.y = q[1] / sin (*angle / 2.0);
	axis.z = q[2] / sin (*angle / 2.0);
}

// --------------------------------------------------------------
// Multiply two quaternions together.
// --------------------------------------------------------------
void multiply_quaternions (float q1[4], float q2[4], float result[4])
{

	result[0] = (q2[3] * q1[0]) + (q2[0] * q1[3]) + (q2[1] * q1[2]) - (q2[2] * q1[1]);
	result[1] = (q2[3] * q1[1]) + (q2[1] * q1[3]) + (q2[2] * q1[0]) - (q2[0] * q1[2]);
	result[2] = (q2[3] * q1[2]) + (q2[2] * q1[3]) + (q2[0] * q1[1]) - (q2[1] * q1[0]);
	
	result[3] = (q2[3] * q1[3]) - (q2[0] * q1[0]) - (q2[1] * q1[1]) - (q2[2] * q1[2]);
}

// --------------------------------------------------------------
// To rotate a vector in 3d space, angle is in radians
// --------------------------------------------------------------
VECTOR3 RotateVector (const VECTOR3& input, double angle, const VECTOR3& rotationaxis) 
{
	// To rotate a vector in 3D space we'll need to build a matrix, these are the variables required to do so.
	MATRIX3 rMatrix;
	double c = cos(angle);
	double s = sin(angle);
	double t = 1.0 - c;
	double x = rotationaxis.x;
	double y = rotationaxis.y;
	double z = rotationaxis.z;

	// Build rotation matrix
	rMatrix.m11 = (t * x * x + c);
	rMatrix.m12 = (t * x * y - s * z);
	rMatrix.m13 = (t * x * z + s * y);
	rMatrix.m21 = (t * x * y + s * z);
	rMatrix.m22 = (t * y * y + c);
	rMatrix.m23 = (t * y * z - s * x);
	rMatrix.m31 = (t * x * z - s * y);
	rMatrix.m32 = (t * y * z + s * x);
	rMatrix.m33 = (t * z * z + c);

	// Perform Rotation
	VECTOR3 output = mul(rMatrix, input); // multiply the input vector by our rotation matrix to get our output vector
	return output; // Return rotated vector
} // End "RotateVector"

// --------------------------------------------------------------
// Spawn a new vessel object (adapted from the Atlantis' ET seperation routine)
// --------------------------------------------------------------
OBJHANDLE SpawnObject (VESSEL *parent, char* name, char* classname, VECTOR3 ofs, double rvel) 
{
	VESSELSTATUS vs;
	parent->GetStatus (vs);

	parent->Local2Rel (ofs, vs.rpos);
	vs.eng_main = vs.eng_hovr = 0.0;
	vs.status = 0;

	VECTOR3 vel = { ofs.x, ofs.y, ofs.z};	// seperation vector
	normalise (vel);
	parent->Local2Rel (ofs, vs.rpos);		

	VECTOR3 rofs;
	parent->GlobalRot (vel, rofs);
	vs.rvel += rofs * rvel;

	oapiCreateVessel (name, classname, vs);
	return oapiGetObjectByName (name);
}


---------- Post added at 15:09 ---------- Previous post was at 15:08 ----------

I'm also confused,

Despite sharing the exact same linker structure I'm only getting errors on the LM, both the CSM and Launch vehicle compile fine.
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
In my simulator code, I have similar files - helpers.h and helpers.cpp that store functions that are used across several classes.

I wrap my headers inside guards like the following
Code:
#ifndef SIM_HELPERS
#define SIM_HELPERS
...
...
code here
...
...
#endif

The ifndef/endif "guards" make sure that my helper code is only included once throughout all of my code and classes. It appears that #pragma once performs the same task, but not all compilers treat it the same way.

I would try replacing #pragma once with
Code:
#ifndef AAPO_COMMON
#define AAPO_COMMON
and inserting
Code:
#endif
at the bottom.

Good luck! :cheers:
 

kuddel

Donator
Donator
Joined
Apr 1, 2008
Messages
2,057
Reaction score
500
Points
113
Found this interesting statement on "cplusplus.com" (which is the best ;) )
#pragma once can fail if the file system contains links to the same file that the compiler cannot follow.

For example, NTFS5 (Windows) supports hard links, but you can't detect that files are linked if you access the file system across an (SMB) network.
http://www.cplusplus.com/forum/beginner/7877/#msg36635
 

Urwumpe

Not funny anymore
Addon Developer
Donator
Joined
Feb 6, 2008
Messages
37,537
Reaction score
2,272
Points
203
Location
Wolfsburg
Preferred Pronouns
Sire
Never had problems with #pragma once on any decent compiler. Using OpenMP pragmas is harder.
 

martins

Orbiter Founder
Orbiter Founder
Joined
Mar 31, 2008
Messages
2,448
Reaction score
462
Points
83
Website
orbit.medphys.ucl.ac.uk
Note that you got a linker error, not a compiler error. The compiler was perfectly happy to compile the function you defined in the .h file into every object file you included it. But then the linker noticed that the function was exported from multiple objects, and complained.

You could just have declared the function static to prevent it from being exported, but that's not a very elegant method. Commonly, you should implement it in a cpp file rather than the header file, as was already mentioned.

The only occasion where function definitions tend to appear in header files is for template functions and classes, since they usually are instantiated on the fly at the places they are invoked, so the function definitions must be available at the calling locations.

Edit: sorry, just noticed that this thread was already covered in cobwebs. Reply may not be relevant any more ...
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Edit: sorry, just noticed that this thread was already covered in cobwebs. Reply may not be relevant any more ...

No problem professor, the reply is appreciated all the same.

In the end I just created a new .cpp file to contain the function bodies and everything seems to be working.
 
Top