C++ DLL 不导出函数

标签 c++ c++11 visual-c++ dll dllexport

我正在开发一个 C++ 库以在 Visual Studio 2013 (C++ 11) 中作为 DLL 导出,我遇到了一个挑战,该库一旦被另一个程序作为外部依赖项导入,包含的类我想要,但是没有包含类的任何功能。

View of classes within external dependency

我已经包含了一个特定的类,它应该是这个 DLL 导出的一部分。

这是我的 RegressionTrainer 类头文件:

#ifndef REGRESSION_TRAINER_H
#define REGRESSION_TRAINER_H
#include "MachineLearning.h"
#pragma once

#ifndef DLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#endif

using namespace MachineLearning;

namespace MachineLearningTraining{

public class RegressionTrainer{
public:
    DLL_EXPORT RegressionTrainer();
    virtual DLL_EXPORT ~RegressionTrainer();

    std::vector<sample_type> DLL_EXPORT getInputData();
    std::vector<double> DLL_EXPORT getAugmentedOutputs();
    std::vector<double> DLL_EXPORT getNonAugmentedOutputs();
protected:
    pugi::xml_parse_result DLL_EXPORT setDataFile(pugi::xml_document &doc, char* file_name);
    void DLL_EXPORT setDataFolder(char* folder_name);
    std::vector<char*> DLL_EXPORT findDataFiles();
    char* data_folder;
    std::vector<char*> file_names;
    std::vector<sample_type> input_data;
    /*
    sample_type m;
    m(0, 0) = 14.86;
    m(1, 0) = 0.24;
    */
    std::vector<double> augmented_outputs;
    std::vector<double> non_augmented_outputs;
    pugi::xml_parse_result result;

    void DLL_EXPORT setInputData();
    void DLL_EXPORT setAugmentedOutputs();
    void DLL_EXPORT setNonAugmentedOutputs();
    virtual int DLL_EXPORT trainAugmentedModel();
    virtual int DLL_EXPORT trainNonAugmentedModel();
};
}
#endif

这里是 MachineLearning.h 的内容:

#include <vector>
#include <iostream>
#include <exception>
#include <fstream>
#include <string>
#include <dlib/svm.h>
#include "pugixml.hpp"

namespace MachineLearning{
// Here we declare that our samples will be 2 dimensional column vectors.  
    typedef dlib::matrix<double, 3, 1> sample_type;

// Now we are making a typedef for the kind of kernel we want to use.  I picked the
// radial basis kernel because it only has one parameter and generally gives good
// results without much fiddling.
    typedef dlib::radial_basis_kernel<sample_type> kernel_type;
}

这是我的 RegressionTrainer.cpp 文件:

#include "Stdafx.h"
#include "RegressionTrainer.h"
#include "dirent.h"

using namespace std;
using namespace dlib;
using namespace MachineLearning;

namespace MachineLearningTraining{

RegressionTrainer::RegressionTrainer(){
    file_names = findDataFiles();
}

RegressionTrainer::~RegressionTrainer(){

}

pugi::xml_parse_result RegressionTrainer::setDataFile(pugi::xml_document &doc, char *file_name){
    return doc.load_file(file_name);
}

void RegressionTrainer::setDataFolder(char *folder_name){
    data_folder = folder_name;
}

std::vector<char*> RegressionTrainer::findDataFiles(){
    DIR *dir;
    struct dirent *ent;
    std::vector<char*> file_names;
    if ((dir = opendir(data_folder)) != NULL) {
        /* print all the files and directories within directory */
        while ((ent = readdir(dir)) != NULL) {
            file_names.push_back(ent->d_name);
        }
        closedir(dir);
    }
    else {
        /* could not open directory */
        perror("Could not open directory");
    }
    return file_names;
}

std::vector<sample_type> RegressionTrainer::getInputData(){
    return input_data;
}

std::vector<double> RegressionTrainer::getAugmentedOutputs(){
    return augmented_outputs;
}

std::vector<double> RegressionTrainer::getNonAugmentedOutputs(){
    return non_augmented_outputs;
}

void RegressionTrainer::setInputData(){
    pugi::xml_document doc;

    for (unsigned i = 0; i < file_names.size(); i++){
        setDataFile(doc, file_names[i]);
        std::cout << "Load result: " << result.description() << endl;

        pugi::xml_node measures = doc.child("case").child("measures");

        sample_type m;
        int count = 0;

        for (pugi::xml_node measure = measures.first_child(); measure; measure = measure.next_sibling()){
            m(count, 0) = measure.text().as_double();
            count++;
        }
        input_data.push_back(m);
    }

}

void RegressionTrainer::setAugmentedOutputs(){
    pugi::xml_document doc;
    for (unsigned i = 0; i < file_names.size(); i++){
        setDataFile(doc, file_names[i]);
        std::cout << "Load result: " << result.description() << endl;

        pugi::xml_node output = doc.child("case").child("studyresults").child("averageangledeviation");
        augmented_outputs.push_back(output.text().as_double());
    }
}

void RegressionTrainer::setNonAugmentedOutputs(){
    pugi::xml_document doc;
    for (unsigned i = 0; i < file_names.size(); i++){
        setDataFile(doc, file_names[i]);
        std::cout << "Load result: " << result.description() << endl;

        pugi::xml_node output = doc.child("case").child("studyresults").child("averageangledeviationAR");
            augmented_outputs.push_back(output.text().as_double());
        }
    }

    int RegressionTrainer::trainAugmentedModel(){
        return 0;
    }

    int RegressionTrainer::trainNonAugmentedModel(){
        return 0;
    }
}

欢迎任何想法!

最佳答案

您的代码令人困惑:

public class RegressionTrainer

这是 C++ 还是 C#?代码的其他部分清楚地表明它是 C++。因此,您必须始终放置准确(或语法相同)的代码。

回到您的问题,您不能导出类​​的成员。您必须从 DLL 导出整个类。

然后问题就开始了。首先,您必须公开(导出与否,无关紧要)类使用的所有类型(例如 pugi::xml_parse_result)。然后,您需要处理不同的编译器版本(甚至 VC2015、各种版本、调试/发布、编译器设置等)。例如,VC2015 调试版本上的 vector 将不同于发布版本。服务包会使问题复杂化。

简而言之:不要导出具有数据成员的整个类。即使整个数据都是私有(private)的,您也需要导出整个类,以便客户端(DLL 的使用者)可以正确编译和链接(到代码)。

那么,解决方案是什么?

好吧,导出一个辅助类:

class DLL_EXPORT RegressionTrainerHelper
{
    RegressionTrainer* pCoreClass;
};

从此类中公开(导出)所有必需的方法(对于客户端)。您只需要将调用从 helper 转移到真正的类。

现在,您可能想知道,您需要导出底层的 type RegressionTrainer,而您又回到了同样的问题。

好吧,是的,不是。如果此帮助程序类在 DLL 中编译,RegressionTrainer 将是真实的。如果没有,只需:

typedef int RegressionTrainer;

任何类型的指针都具有相同的大小(32 位或 64 位)。因此,整个导出的类的大小总是与 DLL 和 EXE 中的大小相匹配。

编辑 例如,有一个 XML 解析器类 ParseXML,但它使用 comlpex 数据成员,您有一个方法 Parse

class ParseXML
{
// complex data members
// some private OR public datamembers and functions, you dont want/need to export
public:
void Parse(const char*); // or something different
};

您可能只想通过辅助类导出Parse

class DLL_EXPORT Exported_ParseXML
{
private:
ParseXML* pCoreXMLInstance;
public:
Exported_ParseXML()
{
  // implementation in CPP
  pCoreXMLInstance = new ParseXML();
}
// The function!
void Parse(const char* pData)
{
  // FORWARD
  pCoreXMLInstance->Parse(pData);
}

客户端将简单地使用导出的类:

Exported_ParseXML parser;
parser.Parse("XML-data");

服务器编译器 (DLL) 会将 ParseXML 视为真正的类类型。但是客户端(EXE 或其他 DLL)需要将 ParseXML 视为 int。你必须弄清楚这一点!

关于C++ DLL 不导出函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36047824/

相关文章:

c++ - boost asio tcp 服务器

c++ - NetUIHWND 和 DirectUIHWND

visual-c++ - 将文件自动放在Visual Studio 2005中的/scripts文件夹中

c++ - 在知道墙壁在哪里的同时计算房间

c++ - vector 中的非连续插入

multithreading - 使用 async 和 future 在 linux (gcc 4.8.4) 上抛出异常

c++ - 从vector <unsigned char>写入PGM文件

c++ - 从初始化列表就地构建 vector (对于带有构造函数参数的类)

c++ - 用 std::transform 填充 std::map(错误:无法推断参数)

c++ - 我应该将哪个版本的 boost 与 c++ visual-studio-2005 一起使用?