У меня есть следующий заголовочный файл
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
. cpp файл, который поддерживает этот класс, прекрасно компилируется. Я могу скомпилировать этот код в виде .exe и внести некоторые изменения, и это то, что я хочу. Однако мне нужно, чтобы этот код вызывался из C#, поэтому я создаю класс-оболочку, который можно вызывать из 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;
}
Однако само включение possion_surface_reconstructor.h
вызывает 114 LNK2005
ошибок, в основном из классов, на которые я ссылаюсь из разных проектов. Примером является
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;
переменная stati c вызывает проблему, и другие ошибки могут быть вызваны теми же проблемами (это не мой код Первоначально и мне нужно обернуть его, чтобы выставить C#).
- Для случая
ThreadPool
почему тип значения struct ThreadPool
, являющийся членом моего класса PossionSurfaceReconstructor
, вызывает эту проблему? - Какой самый простой способ обойти это (не меняя
struct
на class
для каждого случая ошибки?
Заранее спасибо.