extern "C" 解压缩导致奇怪的链接器错误
extern "C" Demangling Causing Strange Linker Errors
我有以下头文件
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
支持这个class 编译好的.cpp 文件。我可以将此代码编译为 .exe 并进行一些调整,这就是我想要的。但是,我需要从 C# 调用此代码,因此我创建了一个可从 C#
调用的包装器 class
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;
}
但是,包含 possion_surface_reconstructor.h
会导致 114 LNK2005
个错误,主要来自 classes 我从不同的项目中引用。一个例子是
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
还有更多。从上面的错误列表中举一个例子 ThreadPool::DefaultSchedule
这是在我引用的包含文件之一中定义的,并且是我的代码库的一部分。此 struct
定义为
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;
};
很明显,static ScheduleType DefaultSchedule;
是一个静态变量导致了一个问题,其他错误很可能是由同样的问题引起的(这不是我最初的代码,我需要将它包装起来以暴露给 C#) .
- 对于
ThreadPool
的情况,为什么我的 PossionSurfaceReconstructor
class 的成员值类型 struct ThreadPool
会导致此问题?
- 最简单的解决方法是什么(不将每个错误实例的
struct
更改为 class
?
提前致谢。
#include
指令实质上是在 #include
出现的位置执行 text-level 复制和粘贴指定文件的内容。您的 header MyMiscellany.h
包含一堆静态成员变量的定义:
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;
一旦这个header包含在多个.cpp文件中,这些变量的定义就会不止一个。一个程序[basic.def.odr]/4中的non-inline函数或变量只能有一个定义,这正是链接器所抱怨的:
2>wrappers.obj : error LNK2005: "private: static bool ThreadPool::_Close" (?_Close@ThreadPool@@0_NA) already defined in poisson_surface_reconstructor.obj
[…]
这个错误信息只是告诉你 ThreadPool::_Close
等,在 wrappers.obj
中出现了一个定义,在 poisson_surface_reconstructor.obj
中已经有一个定义。 poisson_surface_reconstructor.cpp
和 wrappers.cpp
(间接)都包括 MyMiscellany.h
,因此,编译这两个 .cpp 文件产生的两个 object 文件中的每一个都包含所有这些的定义变量。当定义包含 MyMiscellany.h
.
时,它们被粘贴到每个 .cpp 文件中
要解决这个问题,您可以使有问题的变量内联(需要 C++17),在这种情况下,您还可以将定义保留在 class 中,而不必定义变量在 class
之外
class ThreadPool
{
…
inline static bool Close = true;
…
};
或将定义移动到单独的 .cpp 文件(另请参阅 this question 了解更多信息)。
注意:extern "C"
与拆解任何东西都没有关系。它所做的只是声明所讨论的实体应具有 C 语言链接,无论这在目标平台上意味着什么。在 Windows 上,例如 even C linkage may still involve name mangling…
此外,我建议用 std::atomic
.
替换您的 volatile
变量 _RemainingTasks
和自定义 SetAtomic
实现
最后,请注意包含双下划线的标识符以及以下划线开头后跟大写字母的标识符(例如 _Close
)是保留的 [lex.name]/3。实际上不允许在代码中使用这样的标识符。
我有以下头文件
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
支持这个class 编译好的.cpp 文件。我可以将此代码编译为 .exe 并进行一些调整,这就是我想要的。但是,我需要从 C# 调用此代码,因此我创建了一个可从 C#
调用的包装器 classconfiguration.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;
}
但是,包含 possion_surface_reconstructor.h
会导致 114 LNK2005
个错误,主要来自 classes 我从不同的项目中引用。一个例子是
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
还有更多。从上面的错误列表中举一个例子 ThreadPool::DefaultSchedule
这是在我引用的包含文件之一中定义的,并且是我的代码库的一部分。此 struct
定义为
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;
};
很明显,static ScheduleType DefaultSchedule;
是一个静态变量导致了一个问题,其他错误很可能是由同样的问题引起的(这不是我最初的代码,我需要将它包装起来以暴露给 C#) .
- 对于
ThreadPool
的情况,为什么我的PossionSurfaceReconstructor
class 的成员值类型struct ThreadPool
会导致此问题? - 最简单的解决方法是什么(不将每个错误实例的
struct
更改为class
?
提前致谢。
#include
指令实质上是在 #include
出现的位置执行 text-level 复制和粘贴指定文件的内容。您的 header MyMiscellany.h
包含一堆静态成员变量的定义:
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;
一旦这个header包含在多个.cpp文件中,这些变量的定义就会不止一个。一个程序[basic.def.odr]/4中的non-inline函数或变量只能有一个定义,这正是链接器所抱怨的:
2>wrappers.obj : error LNK2005: "private: static bool ThreadPool::_Close" (?_Close@ThreadPool@@0_NA) already defined in poisson_surface_reconstructor.obj
[…]
这个错误信息只是告诉你 ThreadPool::_Close
等,在 wrappers.obj
中出现了一个定义,在 poisson_surface_reconstructor.obj
中已经有一个定义。 poisson_surface_reconstructor.cpp
和 wrappers.cpp
(间接)都包括 MyMiscellany.h
,因此,编译这两个 .cpp 文件产生的两个 object 文件中的每一个都包含所有这些的定义变量。当定义包含 MyMiscellany.h
.
要解决这个问题,您可以使有问题的变量内联(需要 C++17),在这种情况下,您还可以将定义保留在 class 中,而不必定义变量在 class
之外class ThreadPool
{
…
inline static bool Close = true;
…
};
或将定义移动到单独的 .cpp 文件(另请参阅 this question 了解更多信息)。
注意:extern "C"
与拆解任何东西都没有关系。它所做的只是声明所讨论的实体应具有 C 语言链接,无论这在目标平台上意味着什么。在 Windows 上,例如 even C linkage may still involve name mangling…
此外,我建议用 std::atomic
.
volatile
变量 _RemainingTasks
和自定义 SetAtomic
实现
最后,请注意包含双下划线的标识符以及以下划线开头后跟大写字母的标识符(例如 _Close
)是保留的 [lex.name]/3。实际上不允许在代码中使用这样的标识符。