Due to a scheduled maintenance, a maximum 20 minutes license delivery outage may be expected on July 6 2024 (Saturday) between 6PM to 8PM (CEST).
Archicad C++ API
About Archicad add-on development using the C++ API.

Linker error: function already defined

MudratDetector
Enthusiast

At build time, I receive many errors that are similar in nature and alert me of breaking the One Definition Rule, as I understand what is happening.

 

The output from the build is:

 

    Checking File Globs
    ConsultantSheets.cpp
    ConsultantSheetsDialog.cpp
    ConsultantSheetsExcel.cpp
    JHP_Layouts.cpp
    Generating Code...
G:\dev\v27\JHP_Layouts\out\build\x64-Release\JHP_Layouts.obj : error LNK2005: "int __cdecl ACAPI_3D_CreateSight(void * *)" (?ACAPI_3D_CreateSight@@YAHPEAPEAX@Z) already defined in ConsultantSheets.obj

G:\dev\v27\JHP_Layouts\out\build\x64-Release\JHP_Layouts.obj : error LNK2005: "int __cdecl ACAPI_3D_DecomposePgon(int,int * * *)" (?ACAPI_3D_DecomposePgon@@YAHHPEAPEAPEAH@Z) already defined in ConsultantSheets.obj

G:\dev\v27\JHP_Layouts\out\build\x64-Release\JHP_Layouts.obj : error LNK2005: "int __cdecl ACAPI_3D_DeleteSight(void *)" (?ACAPI_3D_DeleteSight@@YAHPEAX@Z) already defined in ConsultantSheets.obj

G:\dev\v27\JHP_Layouts\out\build\x64-Release\JHP_Layouts.obj : error LNK2005: "int __cdecl ACAPI_3D_GetComponent(union API_Component3D *)" (?ACAPI_3D_GetComponent@@YAHPEATAPI_Component3D@@@Z) already defined in ConsultantSheets.obj

G:\dev\v27\JHP_Layouts\out\build\x64-Release\JHP_Layouts.obj : error LNK2005: "int __cdecl ACAPI_3D_GetCurrentWindowSight(void * *)" (?ACAPI_3D_GetCurrentWindowSight@@YAHPEAPEAX@Z) already defined in ConsultantSheets.obj

G:\dev\v27\JHP_Layouts\out\build\x64-Release\JHP_Layouts.obj : error LNK2005: "int __cdecl ACAPI_3D_GetCutPolygonInfo(int,struct API_Plane3D const &,class GS::Array<class Geometry::CustomMultiPolygon2D<class Geometry::PolyId,class Geometry::PolyId,class Geometry::PolyId,class Geometry::PolyId> > *,double *)" (?ACAPI_3D_GetCutPolygonInfo@@YAHHAEBUAPI_Plane3D@@PEAV?$Array@V?$CustomMultiPolygon2D@VPolyId@Geometry@@V12@V12@V12@@Geometry@@@GS@@PEAN@Z) already defined in ConsultantSheets.obj

G:\dev\v27\JHP_Layouts\out\build\x64-Release\JHP_Layouts.obj : error LNK2005: "int __cdecl ACAPI_3D_GetNum(enum API_3DTypeID,int *)" (?ACAPI_3D_GetNum@@YAHW4API_3DTypeID@@PEAH@Z) already defined in ConsultantSheets.obj

G:\dev\v27\JHP_Layouts\out\build\x64-Release\JHP_Layouts.obj : error LNK2005: "int __cdecl ACAPI_3D_SelectSight(void *,void * *)" (?ACAPI_3D_SelectSight@@YAHPEAXPEAPEAX@Z) already defined in ConsultantSheets.obj

 

 

The errors originate from the JHP_Layouts.obj file because of previous definitions in the ConsultantSheets.obj file.  The offensive duplicated functions [ACAPI_3D_CreateSight, ACAPI_3D_DecomposePgon, etc] appear to come from GS header files and have never been flagged in previous ArchiCAD versions of this same Add-On or any of the many other Add-Ons that #include the same header files.

 

From JHP_Layouts.cpp:

 

#pragma once
// GS files
#include "ACAPI_MigrationHeader.hpp"
#include "APIEnvir.h"
#include "ACAPinc.h" // also includes APIdefs.h
// my files
#include "JHP_LayoutsResIDs.hpp"
#include "ConsultantSheetsDialog.h"
#include "..\\..\\functions_27.hpp"

 

From ConsultantSheets.cpp

 

#pragma once

#include "ACAPI_MigrationHeader.hpp"
#include "APIEnvir.h"
#include "ACAPinc.h"
#include "DG.h"
#include "DGFileDialog.hpp"

#include "JHP_LayoutsResIDs.hpp"
#include "ConsultantSheets.h"

#include "FileSystem.hpp"

 

 

I would have also thought that

 

#pragma once

 

in each of the .cpp and .h files would prevent the duplcates.

 

I have deleted and rebuilt the CMake Cache.

I have renamed the ..\<project>\out\build\ folder so that it would recreate from scratch.

From MSDN:  I have read that I can "Use /FORCE:MULTIPLE to create an output file whether or not LINK finds more than one definition for a symbol."  This seems like a workaround and not a true fix of the problem and should be avoided.

Because if the "ACAPI_*" nature of the duplicates, this seems like something I have gotten away with in previous versions of this add-on and in many others.  And I am not sure why this particular add-on is raising such a fuss.

Any point in the right direction would be helpful.

 

Thanks, as always - -

Chris

Chris Gilmer
Intel i9-12950HX CPU @ 2.30GHz, 16 cores
NVIDIA GeForce RTX 3080
48.0 GB RAM
Windows 10 Pro 64-bit
6 REPLIES 6

Hi Chris,

 

#pragma once should really only go into header files. There's no need to put them in .cpp files. Also you should check if all the headers you include in these files have either the #pragma once directive or have include guards. (They serve basically the same job).

 

My guess is that the issue is connected to ACAPI_MigrationHeader.hpp, since the functions ACAPI_3D_* were renamed in the AC27 DevKit and so only show up there. Mentioning that, another issue could be that you are somehow accidentally including headers from the AC26 DevKit (where the ACAPI_3D_* functions are defined also).


So further things to check that I can think of:

  • Check your include directories in your build setup
  • Make sure that you don't #include any .cpp files

Hope that helps,
Bernd

Bernd Schwarzenbacher - Archicad Add-On Developer - Get Add-Ons & Archicad Tips on my Website: Archi-XT.com
MudratDetector
Enthusiast

Hello Bernd,

You continue to teach me the subtle nuances of this complicated machine that is C++.

And it is very much appreciated!

 

 

"#pragma once should really only go into header files."

Good info.  They didn't teach me that in Architecture School.  🙂

I thought I had verified that all header files have a #pragma once line at the top.

I will edit .cpp files and double check the .h and .hpp files

 

"another issue could be that you are somehow accidentally including headers from the AC26 DevKit"

This is possible.  As our ArchiCAD experience has evolved, I will develop an add-on and when we upgrade ArchiCAD, I copy then edit add-on files from the previous version to the current version.  Then copy/edit on to the next version and the next version and so on...  I believe that I identify all of the 'typical GS files' and replace them with equivilent versions from the ..\Examples folder from the current API install.  I will double check all of these.

 

"Check your include directories in your build setup"

Not sure exactly how to do that with a CMake project, but will research.

Should not be too difficult to figure that out.

 

"Make sure that you don't #include any .cpp files"

No problems here.

 

"Hope that helps,"

It always does.  Thanks again.

 

Chris

Chris Gilmer
Intel i9-12950HX CPU @ 2.30GHz, 16 cores
NVIDIA GeForce RTX 3080
48.0 GB RAM
Windows 10 Pro 64-bit
pagliu96
Contributor

Hello Mudrat, did you manage to solve this issue? If so I would like to know more about that.

 

I have the exact same issue, I'm extending the software to AC27 and I tried to add #pragma once to the migration header but this didn't solve my issue.

 

Thanks for opening this topic,

Manuel

 

Update:

I've solved the issue by writing a fix in the ACAPI_MigrationHeader.hpp, basically I added before each function definition the keyword inline.

 

Basically as explained there the problem of this header file is that contains the definition and not just the declaration (usually you split that respectively in .cpp and .h/.hpp). But Microsoft provide an easier solution to that, just prefix the function with the inline keyword which directly substitues the function call with the function itself during compilation (in this way is not really calling the function).

pagliu96_0-1709133962952.png

 

 

 

 

 

MudratDetector
Enthusiast

Good find!  I will dig in to this later today, or perhaps tomorrow and let you know of my success or failure with the same approach.

Thanks for posting the update!

Chris

Chris Gilmer
Intel i9-12950HX CPU @ 2.30GHz, 16 cores
NVIDIA GeForce RTX 3080
48.0 GB RAM
Windows 10 Pro 64-bit

There is even easier fix - you can put the function bodies in anonymous namespace. ACAPI_MigrationHeader.hpp will look like this

#ifndef __GS_ACAPI_MIGRATION_HEADER_HPP__
#define __GS_ACAPI_MIGRATION_HEADER_HPP__

#include "ACAPinc.h"
#include "Polygon2D.hpp"

#define APIDo_LoadLibrariesID APIEnv_SetLibrariesID
#define APIDo_ShowSelectionIn3DID APIIo_ShowSelectionIn3DID
#define APIDo_ShowAllIn3DID APIIo_ShowAllIn3DID
#define APIDo_EditHierarchicalElemID APIIo_EditHierarchicalElemID
#define APIDo_CancelHierarchicalElemID APIIo_CancelHierarchicalElemID
#define APIDo_OkHierarchicalElemID APIIo_OkHierarchicalElemID

namespace
{
    // all functions go here
}

#endif // __GS_ACAPI_MIGRATION_HEADER_HPP__

 

Wow, I didn't knew about that, I will definitely try it.

 

Thanks for this!