Reputation: 4165
I'm able to reference functions from a namespace but, not classes. Here is the namespace file 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);
};
}
I am able to call print_my_name() via SeqLib::print_my_name()
but, not able to reference the FermiAssembler class via SeqLib::FermiAssembler f
Here is the C++ file within my /src
#include <iostream>
#include <Rcpp.h>
#include "SeqLib/FermiAssembler.h"
using namespace std;
// [[Rcpp::export]]
void whats_my_name(){
SeqLib::FermiAssembler f;
Here is the structure of the package
temp
seqLib
SeqLib
src
FermiAssembler.cpp
SeqLib
FermiAssembler.h
headerFiles
SeqLibCommon.h
src
hello_world.cpp
Makevars which contains PKG_CXXFLAGS= -I../SeqLib
Here is FermiAssembler.cpp defined
#include "SeqLib/FermiAssembler.h"
#define MAG_MIN_NSR_COEF .1
namespace SeqLib {
FermiAssembler::~FermiAssembler() {
ClearReads();
ClearContigs();
}
}
The error message is: Error in dyn.load(dllfile) :
unable to load shared object 'temp/seqLib/src/SeqLib.so':
temp/seqLib/src/SeqLib.so: undefined symbol: _ZN6SeqLib14FermiAssemblerD1Ev
update I have moved the entire submodule into the src folder as such:
# temp
# |─── src
# |────SeqLib
# |──────SeqLib
# |────── FermiAssembler.h
# |──────src
# |────── FermiAssembler.cpp
Upvotes: 0
Views: 114
Reputation: 18612
When you see an error referencing something like _ZN6SeqLib14FermiAssemblerD1Ev
, the first step is to run it through a name demangler like c++filt
, which should be included in any Linux distribution:
$ c++filt _ZN6SeqLib14FermiAssemblerD1Ev
# SeqLib::FermiAssembler::~FermiAssembler()
The problem is that in your header file you have declared a destructor for the class FermiAssembler
, but did not provide a definition for it. Your options are
~FermiAssembler() {}
(note the braces, which distinguish this from a declaration). This is equivalent to using the compiler-generated destructor, as described above. FermiAssembler
class does not need a non-default destructor, but for the purpose of demonstration we will explore this option below. Here is the file layout I will be using; you will need to adjust your #include
paths, etc. accordingly:
tree example/
# example/
# ├── DESCRIPTION
# ├── example.Rproj
# ├── NAMESPACE
# ├── R
# │ └── RcppExports.R
# └── src
# ├── demo.cpp
# ├── FermiAssembler.cpp
# ├── FermiAssembler.h
# └── RcppExports.cpp
The header FermiAssembler.h
now becomes:
#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
Notice that I have also converted print_my_name
to a function prototype, so it will also need to be defined in the corresponding source file. Additionally, you can move the previous #include
s to the source file since they are no longer needed here:
// 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
Finally, the file that make use of this class:
// demo.cpp
#include "FermiAssembler.h"
// [[Rcpp::export]]
int whats_my_name() {
SeqLib::FermiAssembler f;
return 0;
}
After building and installing the package, it works as expected:
library(example)
whats_my_name()
# FermiAssembler constructor
# FermiAssembler destructor
# [1] 0
Update Regarding your question of "Can have source files in places other than the top level src/
directory?", yes you can, but I would generally advise against this as it will require a nontrivial Makevars
file. Now using this layout,
tree example
# example
# ├── DESCRIPTION
# ├── example.Rproj
# ├── man
# ├── NAMESPACE
# ├── R
# │ └── RcppExports.R
# ├── SeqLib
# │ ├── SeqLib
# │ │ └── FermiAssembler.h
# │ └── src
# │ └── FermiAssembler.cpp
# └── src
# ├── demo.cpp
# ├── Makevars
# └── RcppExports.cpp
we have in the top level src/
directory (not SeqLib/src
) this Makevars
:
PKG_CXXFLAGS= -I../SeqLib
SOURCES = $(wildcard ../SeqLib/*/*.cpp *.cpp)
OBJECTS = $(wildcard ../SeqLib/*/*.o *.o) $(SOURCES:.cpp=.o)
Note that in the above example we are simply compiling all object files into the same shared library. If you need to, for example, compile intermediate shared or static libraries and link them to the final .so
, then expect your Makevars
to get a lot messier.
Rebuilding and installing,
library(example)
whats_my_name()
# FermiAssembler constructor
# FermiAssembler destructor
# [1] 0
Upvotes: 3