能够在命名空间中引用函数,但不能引用 类
Able To Reference Functions But, Not Classes in a Namespace
我可以从命名空间引用函数,但是 classes。这是命名空间文件 SeqLib/FermiAssembler.h"
#ifndef SEQLIB_FERMI_H
#define SEQLIB_FERMI_H
#include <string>
#include <cstdlib>
#include <iostream>
namespace SeqLib
{
void print_my_name(){ std::cout << "It's Crt" }
class FermiAssembler {
public:
FermiAssembler();
~FermiAssembler();
void AddReads(const BamRecordVector& brv);
};
}
我可以调用 print_my_name() via SeqLib::print_my_name()
,但是无法通过 SeqLib::FermiAssembler f
引用 FermiAssembler class
这是我的 /src
中的 C++ 文件
#include <iostream>
#include <Rcpp.h>
#include "SeqLib/FermiAssembler.h"
using namespace std;
// [[Rcpp::export]]
void whats_my_name(){
SeqLib::FermiAssembler f;
这是包的结构
temp
seqLib
SeqLib
src
FermiAssembler.cpp
SeqLib
FermiAssembler.h
headerFiles
SeqLibCommon.h
src
hello_world.cpp
Makevars which contains PKG_CXXFLAGS= -I../SeqLib
这里是FermiAssembler.cpp定义的
#include "SeqLib/FermiAssembler.h"
#define MAG_MIN_NSR_COEF .1
namespace SeqLib {
FermiAssembler::~FermiAssembler() {
ClearReads();
ClearContigs();
}
}
错误信息是:Error in dyn.load(dllfile) :
无法加载共享对象 'temp/seqLib/src/SeqLib.so':
temp/seqLib/src/SeqLib.so: 未定义的符号:_ZN6SeqLib14FermiAssemblerD1Ev
更新 我已经将整个子模块移动到 src 文件夹中:
# temp
# |─── src
# |────SeqLib
# |──────SeqLib
# |────── FermiAssembler.h
# |──────src
# |────── FermiAssembler.cpp
当您看到引用 _ZN6SeqLib14FermiAssemblerD1Ev
之类的错误时,第一步是 运行 通过 c++filt
之类的名称分解器对其进行 运行,它应该包含在任何 [=108] =]分布:
$ c++filt _ZN6SeqLib14FermiAssemblerD1Ev
# SeqLib::FermiAssembler::~FermiAssembler()
问题是,在您的 header 文件中,您 声明了 class FermiAssembler
的析构函数,但没有提供定义。您的选择是
- 完全删除声明。如果此 class 的析构函数没有做任何特殊的事情,例如释放动态分配的内存或记录信息等,您应该可以使用编译器生成的默认析构函数。但是,如果您像上面那样提供了声明,那么您就是在告诉编译器 "the destructor for this class needs to do something extra so don't generate one for me"。
- 提供一个空的定义:
~FermiAssembler() {}
(注意大括号,它区别于声明)。这相当于使用 compiler-generated 析构函数,如上所述。
- 提供一个non-empty 定义. 在这个简单的例子中
FermiAssembler
class不需要 non-default 析构函数,但出于演示的目的,我们将在下面探讨此选项。
这是我将要使用的文件布局;您需要相应地调整 #include
路径等:
tree example/
# example/
# ├── DESCRIPTION
# ├── example.Rproj
# ├── NAMESPACE
# ├── R
# │ └── RcppExports.R
# └── src
# ├── demo.cpp
# ├── FermiAssembler.cpp
# ├── FermiAssembler.h
# └── RcppExports.cpp
headerFermiAssembler.h
现在变成:
#ifndef SEQLIB_FERMI_H
#define SEQLIB_FERMI_H
namespace SeqLib {
class BamRecordVector;
void print_my_name();
class FermiAssembler {
public:
FermiAssembler();
~FermiAssembler();
void AddReads(const BamRecordVector& brv);
};
} // SeqLib
#endif // SEQLIB_FERMI_H
注意我也把print_my_name
转换成了一个函数原型,所以它也需要在相应的源文件中定义。此外,您可以将之前的 #include
移至源文件,因为此处不再需要它们:
// FermiAssembler.cpp
#include "FermiAssembler.h"
#include <iostream>
#include <Rcpp.h>
namespace SeqLib {
void print_my_name() {
std::cout << "It's Crt";
}
FermiAssembler::FermiAssembler()
{
Rcpp::Rcout << "FermiAssembler constructor\n";
}
FermiAssembler::~FermiAssembler()
{
Rcpp::Rcout << "FermiAssembler destructor\n";
}
} // SeqLib
最后,利用这个class的文件:
// demo.cpp
#include "FermiAssembler.h"
// [[Rcpp::export]]
int whats_my_name() {
SeqLib::FermiAssembler f;
return 0;
}
构建并安装包后,它按预期工作:
library(example)
whats_my_name()
# FermiAssembler constructor
# FermiAssembler destructor
# [1] 0
Update 关于 "Can have source files in places other than the top level src/
directory?" 的问题,是的,你可以,但我通常建议不要这样做,因为它会需要一个重要的 Makevars
文件。现在使用这个布局,
tree example
# example
# ├── DESCRIPTION
# ├── example.Rproj
# ├── man
# ├── NAMESPACE
# ├── R
# │ └── RcppExports.R
# ├── SeqLib
# │ ├── SeqLib
# │ │ └── FermiAssembler.h
# │ └── src
# │ └── FermiAssembler.cpp
# └── src
# ├── demo.cpp
# ├── Makevars
# └── RcppExports.cpp
我们在顶级 src/
目录中(不是 SeqLib/src
)这个Makevars
:
PKG_CXXFLAGS= -I../SeqLib
SOURCES = $(wildcard ../SeqLib/*/*.cpp *.cpp)
OBJECTS = $(wildcard ../SeqLib/*/*.o *.o) $(SOURCES:.cpp=.o)
请注意,在上面的示例中,我们只是将所有 object 文件编译到同一个共享库中。例如,如果您需要编译中间共享库或静态库并将它们 link 编译为最终的 .so
,那么预计您的 Makevars
会变得更加混乱。
重建和安装,
library(example)
whats_my_name()
# FermiAssembler constructor
# FermiAssembler destructor
# [1] 0
我可以从命名空间引用函数,但是 classes。这是命名空间文件 SeqLib/FermiAssembler.h"
#ifndef SEQLIB_FERMI_H
#define SEQLIB_FERMI_H
#include <string>
#include <cstdlib>
#include <iostream>
namespace SeqLib
{
void print_my_name(){ std::cout << "It's Crt" }
class FermiAssembler {
public:
FermiAssembler();
~FermiAssembler();
void AddReads(const BamRecordVector& brv);
};
}
我可以调用 print_my_name() via SeqLib::print_my_name()
,但是无法通过 SeqLib::FermiAssembler f
这是我的 /src
#include <iostream>
#include <Rcpp.h>
#include "SeqLib/FermiAssembler.h"
using namespace std;
// [[Rcpp::export]]
void whats_my_name(){
SeqLib::FermiAssembler f;
这是包的结构
temp
seqLib
SeqLib
src
FermiAssembler.cpp
SeqLib
FermiAssembler.h
headerFiles
SeqLibCommon.h
src
hello_world.cpp
Makevars which contains PKG_CXXFLAGS= -I../SeqLib
这里是FermiAssembler.cpp定义的
#include "SeqLib/FermiAssembler.h"
#define MAG_MIN_NSR_COEF .1
namespace SeqLib {
FermiAssembler::~FermiAssembler() {
ClearReads();
ClearContigs();
}
}
错误信息是:Error in dyn.load(dllfile) :
无法加载共享对象 'temp/seqLib/src/SeqLib.so':
temp/seqLib/src/SeqLib.so: 未定义的符号:_ZN6SeqLib14FermiAssemblerD1Ev
更新 我已经将整个子模块移动到 src 文件夹中:
# temp
# |─── src
# |────SeqLib
# |──────SeqLib
# |────── FermiAssembler.h
# |──────src
# |────── FermiAssembler.cpp
当您看到引用 _ZN6SeqLib14FermiAssemblerD1Ev
之类的错误时,第一步是 运行 通过 c++filt
之类的名称分解器对其进行 运行,它应该包含在任何 [=108] =]分布:
$ c++filt _ZN6SeqLib14FermiAssemblerD1Ev
# SeqLib::FermiAssembler::~FermiAssembler()
问题是,在您的 header 文件中,您 声明了 class FermiAssembler
的析构函数,但没有提供定义。您的选择是
- 完全删除声明。如果此 class 的析构函数没有做任何特殊的事情,例如释放动态分配的内存或记录信息等,您应该可以使用编译器生成的默认析构函数。但是,如果您像上面那样提供了声明,那么您就是在告诉编译器 "the destructor for this class needs to do something extra so don't generate one for me"。
- 提供一个空的定义:
~FermiAssembler() {}
(注意大括号,它区别于声明)。这相当于使用 compiler-generated 析构函数,如上所述。 - 提供一个non-empty 定义. 在这个简单的例子中
FermiAssembler
class不需要 non-default 析构函数,但出于演示的目的,我们将在下面探讨此选项。
这是我将要使用的文件布局;您需要相应地调整 #include
路径等:
tree example/
# example/
# ├── DESCRIPTION
# ├── example.Rproj
# ├── NAMESPACE
# ├── R
# │ └── RcppExports.R
# └── src
# ├── demo.cpp
# ├── FermiAssembler.cpp
# ├── FermiAssembler.h
# └── RcppExports.cpp
headerFermiAssembler.h
现在变成:
#ifndef SEQLIB_FERMI_H
#define SEQLIB_FERMI_H
namespace SeqLib {
class BamRecordVector;
void print_my_name();
class FermiAssembler {
public:
FermiAssembler();
~FermiAssembler();
void AddReads(const BamRecordVector& brv);
};
} // SeqLib
#endif // SEQLIB_FERMI_H
注意我也把print_my_name
转换成了一个函数原型,所以它也需要在相应的源文件中定义。此外,您可以将之前的 #include
移至源文件,因为此处不再需要它们:
// FermiAssembler.cpp
#include "FermiAssembler.h"
#include <iostream>
#include <Rcpp.h>
namespace SeqLib {
void print_my_name() {
std::cout << "It's Crt";
}
FermiAssembler::FermiAssembler()
{
Rcpp::Rcout << "FermiAssembler constructor\n";
}
FermiAssembler::~FermiAssembler()
{
Rcpp::Rcout << "FermiAssembler destructor\n";
}
} // SeqLib
最后,利用这个class的文件:
// demo.cpp
#include "FermiAssembler.h"
// [[Rcpp::export]]
int whats_my_name() {
SeqLib::FermiAssembler f;
return 0;
}
构建并安装包后,它按预期工作:
library(example)
whats_my_name()
# FermiAssembler constructor
# FermiAssembler destructor
# [1] 0
Update 关于 "Can have source files in places other than the top level src/
directory?" 的问题,是的,你可以,但我通常建议不要这样做,因为它会需要一个重要的 Makevars
文件。现在使用这个布局,
tree example
# example
# ├── DESCRIPTION
# ├── example.Rproj
# ├── man
# ├── NAMESPACE
# ├── R
# │ └── RcppExports.R
# ├── SeqLib
# │ ├── SeqLib
# │ │ └── FermiAssembler.h
# │ └── src
# │ └── FermiAssembler.cpp
# └── src
# ├── demo.cpp
# ├── Makevars
# └── RcppExports.cpp
我们在顶级 src/
目录中(不是 SeqLib/src
)这个Makevars
:
PKG_CXXFLAGS= -I../SeqLib
SOURCES = $(wildcard ../SeqLib/*/*.cpp *.cpp)
OBJECTS = $(wildcard ../SeqLib/*/*.o *.o) $(SOURCES:.cpp=.o)
请注意,在上面的示例中,我们只是将所有 object 文件编译到同一个共享库中。例如,如果您需要编译中间共享库或静态库并将它们 link 编译为最终的 .so
,那么预计您的 Makevars
会变得更加混乱。
重建和安装,
library(example)
whats_my_name()
# FermiAssembler constructor
# FermiAssembler destructor
# [1] 0