extern "C" Demangling, вызывающий странные ошибки компоновщика - PullRequest
1 голос
/ 21 января 2020

У меня есть следующий заголовочный файл

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#).

  1. Для случая ThreadPool почему тип значения struct ThreadPool, являющийся членом моего класса PossionSurfaceReconstructor, вызывает эту проблему?
  2. Какой самый простой способ обойти это (не меняя struct на class для каждого случая ошибки?

Заранее спасибо.

1 Ответ

2 голосов
/ 21 января 2020

Директива #include, по сути, выполняет копирование и вставку содержимого указанного файла на уровне текста в точке, где появляется #include. Ваш заголовок MyMiscellany.h содержит определения для нескольких переменных-членов stati c:

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;

В тот момент, когда этот заголовок включен в несколько файлов. cpp, будет более одного определения из этих переменных. В программе [basi c .def.odr] / 4 может быть только одно определение не встроенной функции или переменной: именно на это жалуется компоновщик:

2> wrappers.obj: ошибка LNK2005: «private: stati c bool ThreadPool :: _ Close» (? _Close@ThreadPool@@0_NA) уже определен в poisson_surface_reconstructor.obj
[…]

Это сообщение об ошибке просто говорит вам, что ThreadPool::_Close, et c., Для которого есть определение в wrappers.obj, уже имеет определение в poisson_surface_reconstructor.obj. И poisson_surface_reconstructor.cpp, и wrappers.cpp (косвенно) включали MyMiscellany.h, таким образом, каждый из двух объектных файлов, полученных в результате компиляции этих двух. cpp файлов, содержит определения для всех этих переменных. Определения были вставлены в каждый файл. cpp, когда они включали MyMiscellany.h.

Чтобы решить эту проблему, вы можете сделать соответствующие переменные встроенными (требуется C ++ 17), и в этом случае вы также может хранить определение внутри класса вместо того, чтобы определять переменную вне класса

class ThreadPool
{
    …
    inline static bool Close = true;
    …
};

или перемещать определение в отдельный файл. cpp (см. также этот вопрос подробнее об этом).


Примечание: extern "C" не имеет ничего общего с разборкой чего-либо. Все, что он делает, это заявляет, что рассматриваемая сущность должна иметь языковую связь C, что бы это ни значило на целевой платформе. На Windows, например, даже C связь все еще может включать искажение имени

Кроме того, я бы предложил заменить вашу volatile переменную _RemainingTasks и пользовательскую SetAtomic реализация с std::atomic.

Наконец, имейте в виду, что идентификаторы, которые содержат двойное подчеркивание, а также идентификаторы, которые начинаются со знака подчеркивания, за которым следует заглавная буква, такая как, например, _Close, зарезервированы [lex.name] / 3 . На самом деле вы не можете использовать идентификаторы в своем коде.

...