Reputation: 23833
I have the following header file
possion_surface_reconstructor.h
#ifndef POISSON_SURFACE_RECONSTRUCTOR_H
#define POISSON_SURFACE_RECONSTRUCTOR_H
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include <iostream>
#include <boost/format.hpp>
#include "../core/PreProcessor.h"
#include "../core/MyMiscellany.h"
#include "../core/CmdLineParser.h"
#include "../core/PPolynomial.h"
#include "../core/FEMTree.h"
#include "../core/Ply.h"
#include "../core/PointStreamData.h"
#include "../core/Image.h"
#include "callbacks.h"
namespace MeshingAdapter
{
#undef USE_DOUBLE // If enabled, double-precesion is used
#define DATA_DEGREE 0 // The order of the B-Spline used to splat in data for color interpolation
#define WEIGHT_DEGREE 2 // The order of the B-Spline used to splat in the weights for density estimation
#define NORMAL_DEGREE 2 // The order of the B-Spline used to splat in the normals for constructing the Laplacian constraints
#define DEFAULT_FEM_DEGREE 1 // The default finite-element degree
#define DEFAULT_FEM_BOUNDARY BOUNDARY_NEUMANN // The default finite-element boundary type
#define DIMENSION 3
cmdLineParameter< char* >
In("in"),
Out("out"),
TempDir("tempDir"),
Grid("grid"),
Tree("tree"),
Transform("xForm");
cmdLineReadable
Performance("performance"),
ShowResidual("showResidual"),
NoComments("noComments"),
PolygonMesh("polygonMesh"),
NonManifold("nonManifold"),
ASCII("ascii"),
Density("density"),
LinearFit("linearFit"),
PrimalGrid("primalGrid"),
ExactInterpolation("exact"),
Normals("normals"),
Colors("colors"),
InCore("inCore"),
Verbose("verbose");
cmdLineParameter< int >
#ifndef FAST_COMPILE
Degree("degree", DEFAULT_FEM_DEGREE),
#endif // !FAST_COMPILE
Depth("depth", 8),
KernelDepth("kernelDepth"),
Iters("iters", 8),
FullDepth("fullDepth", 5),
BaseDepth("baseDepth", 0),
BaseVCycles("baseVCycles", 1),
#ifndef FAST_COMPILE
BType("bType", DEFAULT_FEM_BOUNDARY + 1),
#endif // !FAST_COMPILE
MaxMemoryGB("maxMemory", 0),
#ifdef _OPENMP
ParallelType("parallel", (int)ThreadPool::OPEN_MP),
#else // !_OPENMP
ParallelType("parallel", (int)ThreadPool::THREAD_POOL),
#endif // _OPENMP
ScheduleType("schedule", (int)ThreadPool::DefaultSchedule),
ThreadChunkSize("chunkSize", (int)ThreadPool::DefaultChunkSize),
Threads("threads", (int)std::thread::hardware_concurrency());
cmdLineParameter< float >
DataX("data", 32.f),
SamplesPerNode("samplesPerNode", 1.5f),
Scale("scale", 1.1f),
Width("width", 0.f),
Confidence("confidence", 0.f),
ConfidenceBias("confidenceBias", 0.f),
CGSolverAccuracy("cgAccuracy", 1e-3f),
PointWeight("pointWeight");
cmdLineReadable* params[] =
{
#ifndef FAST_COMPILE
&Degree,
&BType,
#endif // !FAST_COMPILE
&In,
&Depth,
&Out,
&Transform,
&Width,
&Scale,
&Verbose,
&CGSolverAccuracy,
&NoComments,
&KernelDepth,
&SamplesPerNode,
&Confidence,
&NonManifold,
&PolygonMesh,
&ASCII,
&ShowResidual,
&ConfidenceBias,
&BaseDepth,
&BaseVCycles,
&PointWeight,
&Grid,
&Threads,
&Tree,
&Density,
&FullDepth,
&Iters,
&DataX,
&Colors,
&Normals,
&LinearFit,
&PrimalGrid,
&TempDir,
&ExactInterpolation,
&Performance,
&MaxMemoryGB,
&InCore,
&ParallelType,
&ScheduleType,
&ThreadChunkSize,
NULL
};
class PoissonSurfaceReconstructor
{
public:
PoissonSurfaceReconstructor(
ProgressCallback& progressCallback,
WarningCallback& warningCallback,
ErrorCallback& errorCallback);
bool ExecuteSurfaceReconstruction(int argc, char* argv[]);
private:
template< unsigned int Dim, class Real >
struct FEMTreeProfiler;
template< unsigned int Dim, typename Real >
struct ConstraintDual;
template< unsigned int Dim, typename Real >
struct SystemDual;
template< unsigned int Dim >
struct SystemDual< Dim, double >;
template< class Real, typename ... SampleData, unsigned int ... FEMSigs >
int Execute(int argc, char* argv[], UIntPack< FEMSigs ... >);
template< unsigned int Dim, class Real, typename ... SampleData >
int Execute(int argc, char* argv[]);
template< typename Real, unsigned int Dim >
int WriteGrid(ConstPointer(Real) values, int res, const char* fileName);
template< typename Vertex, typename Real, typename SetVertexFunction, unsigned int ... FEMSigs, typename ... SampleData >
int ExtractMesh(
UIntPack< FEMSigs ... >,
std::tuple< SampleData ... >,
FEMTree< sizeof ... (FEMSigs), Real >& tree,
const DenseNodeData< Real, UIntPack< FEMSigs ... > >& solution,
Real isoValue,
const std::vector< typename FEMTree< sizeof ... (FEMSigs),
Real >::PointSample >* samples,
std::vector< MultiPointStreamData< Real, PointStreamNormal< Real, DIMENSION >,
MultiPointStreamData< Real, SampleData ... > > >* sampleData,
const typename FEMTree< sizeof ... (FEMSigs), Real >::template DensityEstimator< WEIGHT_DEGREE >* density,
const SetVertexFunction& SetVertex,
std::vector< std::string >& comments,
XForm< Real, sizeof...(FEMSigs) + 1 > iXForm);
template< class Real, unsigned int Dim >
XForm< Real, Dim + 1 > GetBoundingBoxXForm(Point< Real, Dim > min, Point< Real, Dim > max, Real scaleFactor);
template< class Real, unsigned int Dim >
XForm< Real, Dim + 1 > GetBoundingBoxXForm(
Point< Real, Dim > min,
Point< Real, Dim > max,
Real width,
Real scaleFactor,
int& depth);
template< class Real, unsigned int Dim >
XForm< Real, Dim + 1 > GetPointXForm(InputPointStream< Real, Dim >& stream, Real width, Real scaleFactor, int& depth);
template< class Real, unsigned int Dim >
XForm< Real, Dim + 1 > GetPointXForm(InputPointStream< Real, Dim >& stream, Real scaleFactor);
template<typename... Arguments>
std::string FormatString(const std::string& fmt, const Arguments&... args);
double Weight(double v, double start, double end);
const float DefaultPointWeightMultiplier = 2.f;
ProgressCallback _progressCallback;
WarningCallback _warningCallback;
ErrorCallback _errorCallback;
};
}
#endif // POISSON_SURFACE_RECONSTRUCTOR_H
The .cpp file that back this class compiles fine. I can compile this code as an .exe and make some tweaks and it's what I want. However, I need this code to be called from C# so I create a wrapper class that is callable from C#
configuration.h
#ifndef MESHING_DLL_CONFIG_H
#define MESHING_DLL_CONFIG_H
#if defined(_MSC_VER)
# define LIBRARY_EXPORT __declspec(dllexport)
# define LIBRARY_IMPORT __declspec(dllimport)
#elif defined(__GNUC__) && __GNUC__ > 3
# define LIBRARY_EXPORT __attribute__((visibility("default")))
# define LIBRARY_IMPORT __attribute__((visibility("default")))
#else
# define LIBRARY_EXPORT
# define LIBRARY_IMPORT
#endif
#ifdef LIBRARY_API_EXPORTS
# define LIBRARY_API LIBRARY_EXPORT
#elif LIBRARY_API_IMPORTS
# define LIBRARY_API LIBRARY_IMPORT
#else
# define LIBRARY_API
#endif
#endif /* MESHING_DLL_CONFIG_H */
wrappers.h
#ifndef MESHING_WRAPPERS_H
#define MESHING_WRAPPERS_H
#include <exception>
#include "configuration.h"
#include "callbacks.h"
#include "poisson_surface_reconstructor.h"
typedef intptr_t ArrayHandle;
extern "C"
{
LIBRARY_EXPORT bool ExecutePoissonSurfaceReconstruction(
int argc,
char* argv[],
ProgressCallback progressCallback,
WarningCallback warningCallback,
ErrorCallback errorCallback);
LIBRARY_EXPORT bool Release(ArrayHandle arrayHandle);
}
#endif // MESHING_WRAPPERS_H
wrappers.cpp
#include "wrappers.h"
using namespace MeshingAdapter;
LIBRARY_EXPORT bool ExecutePoissonSurfaceReconstruction(
int argc,
char* argv[],
ProgressCallback progressCallback,
WarningCallback warningCallback,
ErrorCallback errorCallback)
{
try
{
PoissonSurfaceReconstructor* poissonSurfaceRecon =
new PoissonSurfaceReconstructor(
progressCallback,
warningCallback,
errorCallback);
bool success = poissonSurfaceRecon->ExecuteSurfaceReconstruction(argc, argv);
delete poissonSurfaceRecon;
return success;
}
catch (const std::exception& ex)
{
errorCallback(ex.what());
return false;
}
}
LIBRARY_EXPORT bool Release(ArrayHandle arrayHandle)
{
// #TODO can we do this smarter?
return true;
}
However, the very inclusion of possion_surface_reconstructor.h
causes 114 LNK2005
errors, mainly from classes I reference from different projects. An example being
2>wrappers.obj : error LNK2005: "private: static bool ThreadPool::_Close" (?_Close@ThreadPool@@0_NA) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "public: static unsigned __int64 ThreadPool::DefaultChunkSize" (?DefaultChunkSize@ThreadPool@@2_KA) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "private: static class std::vector<class std::thread,class std::allocator<class std::thread> > ThreadPool::_Threads" (?_Threads@ThreadPool@@0V?$vector@Vthread@std@@V?$allocator@Vthread@std@@@2@@std@@A) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "private: static class std::condition_variable ThreadPool::_DoneWithWork" (?_DoneWithWork@ThreadPool@@0Vcondition_variable@std@@A) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "public: static int const (* HyperCube::MarchingSquares::edges)[5]" (?edges@MarchingSquares@HyperCube@@2QAY04$$CBHA) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "char const * * type_names" (?type_names@@3PAPEBDA) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "char const * * BoundaryNames" (?BoundaryNames@@3PAPEBDA) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "char const * * ShowGlobalResidualNames" (?ShowGlobalResidualNames@@3PAPEBDA) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "private: static unsigned int volatile ThreadPool::_RemainingTasks" (?_RemainingTasks@ThreadPool@@0IC) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "public: static char const * const StackTracer::exec" (?exec@StackTracer@@2PEBDEB) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "public: static char const * const ImageWriterParams::DefaultTileExtension" (?DefaultTileExtension@ImageWriterParams@@2PEBDEB) already defined in poisson_surface_reconstructor.obj
2>wrappers.obj : error LNK2005: "public: static enum ThreadPool::ScheduleType ThreadPool::DefaultSchedule" (?DefaultSchedule@ThreadPool@@2W4ScheduleType@1@A) already defined in poisson_surface_reconstructor.obj
And more. Taking one example from the above list of errors ThreadPool::DefaultSchedule
this is defined in one of the include files I reference and is part of my code base. This struct
is defined as
struct ThreadPool
{
enum ParallelType
{
#ifdef _OPENMP
OPEN_MP ,
#endif // _OPENMP
THREAD_POOL ,
ASYNC ,
NONE
};
static const std::vector< std::string > ParallelNames;
enum ScheduleType
{
STATIC ,
DYNAMIC
};
static const std::vector< std::string > ScheduleNames;
static size_t DefaultChunkSize;
static ScheduleType DefaultSchedule;
template< typename ... Functions >
static void ParallelSections( const Functions & ... functions )
{
std::vector< std::future< void > > futures( sizeof...(Functions) );
_ParallelSections( &futures[0] , functions ... );
for( size_t t=0 ; t<futures.size() ; t++ ) futures[t].get();
}
static void Parallel_for( size_t begin , size_t end , const std::function< void ( unsigned int , size_t ) > &iterationFunction , ScheduleType schedule=DefaultSchedule , size_t chunkSize=DefaultChunkSize )
{
if( begin>=end ) return;
size_t range = end - begin;
size_t chunks = ( range + chunkSize - 1 ) / chunkSize;
unsigned int threads = (unsigned int)NumThreads();
std::atomic< size_t > index;
index.store( 0 );
if( range<chunkSize || _ParallelType==NONE || threads==1 )
{
for( size_t i=begin ; i<end ; i++ ) iterationFunction( 0 , i );
return;
}
auto _ChunkFunction = [ &iterationFunction , begin , end , chunkSize ]( unsigned int thread , size_t chunk )
{
const size_t _begin = begin + chunkSize*chunk;
const size_t _end = std::min< size_t >( end , _begin+chunkSize );
for( size_t i=_begin ; i<_end ; i++ ) iterationFunction( thread , i );
};
auto _StaticThreadFunction = [ &_ChunkFunction , chunks , threads ]( unsigned int thread )
{
for( size_t chunk=thread ; chunk<chunks ; chunk+=threads ) _ChunkFunction( thread , chunk );
};
auto _DynamicThreadFunction = [ &_ChunkFunction , chunks , &index ]( unsigned int thread )
{
size_t chunk;
while( ( chunk=index.fetch_add(1) )<chunks ) _ChunkFunction( thread , chunk );
};
if ( schedule==STATIC ) _ThreadFunction = _StaticThreadFunction;
else if( schedule==DYNAMIC ) _ThreadFunction = _DynamicThreadFunction;
if( false ){}
#ifdef _OPENMP
else if( _ParallelType==OPEN_MP )
{
if( schedule==STATIC )
#pragma omp parallel for num_threads( threads ) schedule( static , 1 )
for( int c=0 ; c<chunks ; c++ ) _ChunkFunction( omp_get_thread_num() , c );
else if( schedule==DYNAMIC )
#pragma omp parallel for num_threads( threads ) schedule( dynamic , 1 )
for( int c=0 ; c<chunks ; c++ ) _ChunkFunction( omp_get_thread_num() , c );
}
#endif // _OPENMP
else if( _ParallelType==ASYNC )
{
static std::vector< std::future< void > > futures;
futures.resize( threads-1 );
for( unsigned int t=1 ; t<threads ; t++ ) futures[t-1] = std::async( std::launch::async , _ThreadFunction , t );
_ThreadFunction( 0 );
for( unsigned int t=1 ; t<threads ; t++ ) futures[t-1].get();
}
else if( _ParallelType==THREAD_POOL )
{
unsigned int targetTasks = 0;
if( !SetAtomic( &_RemainingTasks , threads-1 , targetTasks ) )
{
WARN( "nested for loop, reverting to serial" );
for( size_t i=begin ; i<end ; i++ ) iterationFunction( 0 , i );
}
else
{
_WaitingForWorkOrClose.notify_all();
{
std::unique_lock< std::mutex > lock( _Mutex );
_DoneWithWork.wait( lock , [&]( void ){ return _RemainingTasks==0; } );
}
}
}
}
static unsigned int NumThreads( void ){ return (unsigned int)_Threads.size()+1; }
static void Init( ParallelType parallelType , unsigned int numThreads=std::thread::hardware_concurrency() )
{
_ParallelType = parallelType;
if( _Threads.size() && !_Close )
{
_Close = true;
_WaitingForWorkOrClose.notify_all();
for( unsigned int t=0 ; t<_Threads.size() ; t++ ) _Threads[t].join();
}
_Close = true;
numThreads--;
_Threads.resize( numThreads );
if( _ParallelType==THREAD_POOL )
{
_RemainingTasks = 0;
_Close = false;
for( unsigned int t=0 ; t<numThreads ; t++ ) _Threads[t] = std::thread( _ThreadInitFunction , t );
}
}
static void Terminate( void )
{
if( _Threads.size() && !_Close )
{
_Close = true;
_WaitingForWorkOrClose.notify_all();
for( unsigned int t=0 ; t<_Threads.size() ; t++ ) _Threads[t].join();
_Threads.resize( 0 );
}
}
private:
ThreadPool( const ThreadPool & ){}
ThreadPool &operator = ( const ThreadPool & ){ return *this; }
template< typename Function >
static void _ParallelSections( std::future< void > *futures , const Function &function ){ *futures = std::async( std::launch::async , function ); }
template< typename Function , typename ... Functions >
static void _ParallelSections( std::future< void > *futures , const Function &function , const Functions& ... functions )
{
*futures = std::async( std::launch::async , function );
_ParallelSections( futures+1 , functions ... );
}
static void _ThreadInitFunction( unsigned int thread )
{
// Wait for the first job to come in
std::unique_lock< std::mutex > lock( _Mutex );
_WaitingForWorkOrClose.wait( lock );
while( !_Close )
{
lock.unlock();
// do the job
_ThreadFunction( thread );
// Notify and wait for the next job
lock.lock();
_RemainingTasks--;
if( !_RemainingTasks ) _DoneWithWork.notify_all();
_WaitingForWorkOrClose.wait( lock );
}
}
static bool _Close;
static volatile unsigned int _RemainingTasks;
static std::mutex _Mutex;
static std::condition_variable _WaitingForWorkOrClose , _DoneWithWork;
static std::vector< std::thread > _Threads;
static std::function< void ( unsigned int ) > _ThreadFunction;
static ParallelType _ParallelType;
};
Clearly static ScheduleType DefaultSchedule;
being a static variable is causing a problem and the other errors are likely to be caused by the same issues (this is not my code originally and I need to wrap it to expose to C#).
ThreadPool
why is the value type struct ThreadPool
that is a memeber of my PossionSurfaceReconstructor
class causing this problem? struct
to class
for each instance of the error? Thanks in advance.
Upvotes: 1
Views: 403
Reputation: 15943
The #include
directive essentially performs text-level copy and paste of the contents of the specified file at the point where the #include
appears. Your header MyMiscellany.h
contains definitions for a bunch of static member variables:
size_t ThreadPool::DefaultChunkSize = 128;
ThreadPool::ScheduleType ThreadPool::DefaultSchedule = ThreadPool::DYNAMIC;
bool ThreadPool::_Close;
volatile unsigned int ThreadPool::_RemainingTasks;
std::mutex ThreadPool::_Mutex;
std::condition_variable ThreadPool::_WaitingForWorkOrClose;
std::condition_variable ThreadPool::_DoneWithWork;
std::vector< std::thread > ThreadPool::_Threads;
std::function< void ( unsigned int ) > ThreadPool::_ThreadFunction;
ThreadPool::ParallelType ThreadPool::_ParallelType;
The moment this header is included into more than one .cpp file, there will be more than one definition of these variables. There can be only one definition of a non-inline function or variable in a program [basic.def.odr]/4, which is exactly what the linker complains about:
2>wrappers.obj : error LNK2005: "private: static bool ThreadPool::_Close" (?_Close@ThreadPool@@0_NA) already defined in poisson_surface_reconstructor.obj
[…]
This error message just tells you that ThreadPool::_Close
, etc., for which there appears a definition in wrappers.obj
, already had a definition in poisson_surface_reconstructor.obj
. Both, poisson_surface_reconstructor.cpp
as well as wrappers.cpp
(indirectly) included MyMiscellany.h
, thus, each of the two object files resulting from compiling these two .cpp files contains definitions for all these variables. The definitions were pasted into each .cpp file when they included MyMiscellany.h
.
To solve this problem, you can either make the variables in question inline (requires C++17), in which case you can also keep the definition inside the class rather than having to define the variable outside of the class
class ThreadPool
{
…
inline static bool Close = true;
…
};
or move the definition to a separate .cpp file (see also this question for more on that).
Note: extern "C"
has got nothing to do with demangling anything. All it does is declare that the entity in question shall have C language linkage, whatever that means on the target platform. On Windows, for example, even C linkage may still involve name mangling…
Furthermore, I would suggest to replace your volatile
variable _RemainingTasks
and custom SetAtomic
implementation with std::atomic
.
Finally, be aware that identifiers that contain a double underscore as well as identifiers that begin with an underscore followed by an uppercase letter such as, e.g., _Close
, are reserved [lex.name]/3. You are actually not allowed to use identifiers like that in your code.
Upvotes: 2