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
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
Remove the declaration entirely. If the destructor for this class is not doing anything special, such as freeing dynamically allocated memory, or logging information, etc., you should be fine with the default destructor generated by the compiler. However, if you provide a declaration such as you did above, you are telling the compiler "the destructor for this class needs to do something extra so don't generate one for me".
Provide an empty definition: ~FermiAssembler() {} (note the braces, which distinguish this from a declaration). This is equivalent to using the compiler-generated destructor, as described above.
Provide a non-empty definition. In this simple example the 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 #includes 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
Related
I am currently learning C and am trying to understand the possibilities of dynamic libraries.
My current question is, if I have a simple "Hello World" application in C called "ProgA", and this program dynamically loads a shared library with some example code called "LibB", can LibB access a global variable in ProgA, which was declared as external?
Given is the following example code for demonstration of the problem:
file header.h
#ifndef TEST_H
#define TEST_H
typedef struct test_import_s {
int some_field;
} test_import_t;
extern test_import_t newtestimport;
#endif
file prog_a.c
#include <stdio.h>
#include <windows.h>
#include "header.h"
test_import_t newtestimport = {
.some_field = 42
};
int main()
{
HINSTANCE hinstLib;
typedef void (*FunctionPointer)();
newtestimport.some_field = 42;
hinstLib = LoadLibrary("lib_b.dll");
if (hinstLib != NULL)
{
FunctionPointer initialize_lib_b;
initialize_lib_b = (FunctionPointer)GetProcAddress(hinstLib, "initialize_lib_b");
if (initialize_lib_b != NULL)
{
initialize_lib_b();
}
FreeLibrary(hinstLib);
}
return 0;
}
file lib_b.c
#include <stdio.h>
#include "header.h"
test_import_t *timp;
void initialize_lib_b() {
timp = &newtestimport;
int some_field = timp->some_field;
printf("Result from function: %d\n", some_field);
}
file CMakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(dynamic-library-2 C)
set(CMAKE_C_STANDARD 23)
add_library(lib_b SHARED lib_b.c)
set_target_properties(lib_b PROPERTIES PREFIX "" OUTPUT_NAME "lib_b")
add_executable(prog_a prog_a.c)
target_link_libraries(prog_a lib_b)
In the above example, the headerfile header.h defines the struct test_import_t and an external variable newtestimport using this struct. In the C file of the main program prog_a.c one property of this struct is assigned the value 42. It then dynamically loads the library lib_b.c using the Windows API and executes a function in it. The function then should access the variable newtestimport of the main program and print out the value of the variable (42).
This example does not work. The compiler throws the following error:
====================[ Build | prog_a | Debug ]==================================
C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\cmake\win\x64\bin\cmake.exe --build C:\Users\user1\projects\learning-c\cmake-build-debug --target prog_a -j 9
[1/2] Linking C shared library dynamic-library-2\lib_b.dll
FAILED: dynamic-library-2/lib_b.dll dynamic-library-2/liblib_b.dll.a
cmd.exe /C "cd . && C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\mingw\bin\gcc.exe -fPIC -g -Wl,--export-all-symbols -shared -o dynamic-library-2\lib_b.dll -Wl,--out-implib,dynamic-library-2\liblib_b.dll.a -Wl,--major-image-version,0,--minor-image-version,0 dynamic-library-2/CMakeFiles/lib_b.dir/lib_b.c.obj -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cd ."
C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\mingw\bin/ld.exe: dynamic-library-2/CMakeFiles/lib_b.dir/lib_b.c.obj:lib_b.c:(.rdata$.refptr.newtestimport[.refptr.newtestimport]+0x0): undefined reference to `newtestimport'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
How can the example be fixed to accomplish the described goal?
Windows DLLs are self-contained, and can not have undefined references similar to newtestimport, unless these references are satisfied by another DLL.
How can the example be fixed to accomplish the described goal?
The best fix is to pass the address of newtestimport into the function that needs it (initialize_lib_b() here).
If for some reason you can't do that, your next best option is to define the newtestimport as a dllexport variable in another DLL, e.g. lib_c.dll.
Then both the main executable and lib_b.dll would be linked against lib_c.lib, and would both use that variable from lib_c.dll.
P.S. Global variables are a "code smell" and a significant source of bugs. You should avoid them whenever possible, and in your example there doesn't seem to be any good reason to use them.
I am trying to build a Rcpp package. In my Rcpp function, I am trying to call the R function written by myself (not from any package, for here sum_matrix_row for example) in my Rcpp function. For that R function, I have put it in the /package/R/ folder.
To make it simple, I will give an easy example here, my R function:
sum_matrix_row <- function(x) {return(matrix(apply(x, 1, sum), ncol = 1))}
And my Rcpp function (called test_function), I put it in the /package/src/ folder:
#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]
using namespace Rcpp;
Function sum_matrix_row = Environment::global_env()["sum_matrix_row"];
// [[Rcpp::export]]
List asmbPLS_fit_rcpp(arma::mat test_matrix) {
aram::colvec matrix_row = sum_matrix_row(test_matrix)
List output = List::create(_["matrix_row"] = matrix_row);
return (output);
}
This one always works when I load sum_matrix_row function first and then sourceCpp my Rcpp function, but this cannot work when I build the Rcpp package. Does anyone have some suggestions on that? Thanks a lot!
Edit April 20 2022 to provide more details on my question
packagetest
├── DESCRIPTION
├── NAMESPACE
├── man
│ ├── rcpparma_hello_world.Rd
│ └── packagetest-package.Rd
├── R
│ ├── sum_matrix_row.R
│ └── RcppExports.R
└── src
├── Makevars
├── Makevars.win
├── rcpparma_hello_world.cpp
├── RcppExports.cpp
├── RcppExports.o
└── test_function.cpp
The codes I used to create these files are:
RcppArmadillo.package.skeleton("packagetest")
setwd("~/Rpackage/packagetest")
Rcpp::compileAttributes()
devtools::document()
And in my Rcpp function, I have changed my codes to:
Function sum_matrix_row = Environment::namespace_env(packagetest)["sum_matrix_row"]
to call my R function.
Then I got the errors like:
test_function.cpp:5:54: error: 'packagetest' was not declared in this scope
Function sum_matrix_row = Environment::namespace_env(packagetest)["sum_matrix_row"];
^~~~~~~~~~~
test_function.cpp:5:54: note: suggested alternative: 'clock_getres'
Function sum_matrix_row = Environment::namespace_env(packagetest)["sum_matrix_row"];
^~~~~~~~~~~
clock_getres
test_function.cpp: In function 'Rcpp::List asmbPLS_fit_rcpp(arma::mat)':
test_function.cpp:10:3: error: 'aram' has not been declared
aram::colvec matrix_row = sum_matrix_row(test_matrix);
^~~~
test_function.cpp:12:48: error: 'matrix_row' was not declared in this scope
List output = List::create(_["matrix_row"] = matrix_row);
^~~~~~~~~~
test_function.cpp:12:48: note: suggested alternative: 'sum_matrix_row'
List output = List::create(_["matrix_row"] = matrix_row);
^~~~~~~~~~
sum_matrix_row
make: *** [C:/PROGRA~1/R/R-41~1.0/etc/x64/Makeconf:245: test_function.o] Error 1
ERROR: compilation failed for package 'packagetest'
Does anyone have any ideas on these?
Edit April 20 2022 according to #user20650 's suggestion:
My updated codes for my rcpp function, I add quote for packagetest, and add a convert function as<>:
#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]
using namespace Rcpp;
using namespace arma;
Rcpp::Function sum_matrix_row = Environment::namespace_env("packagetest")["sum_matrix_row"];
// [[Rcpp::export]]
Rcpp::List asmbPLS_fit_rcpp(arma::mat test_matrix) {
aram::colvec matrix_row = as<aram::colvec>(sum_matrix_row(test_matrix));
List output = List::create(_["matrix_row"] = matrix_row);
return (output);
}
After this, I still get some errors:
test_function.cpp: In function 'Rcpp::List asmbPLS_fit_rcpp(arma::mat)':
test_function.cpp:11:3: error: 'aram' has not been declared
aram::colvec matrix_row = as<aram::colvec>(sum_matrix_row(test_matrix));
^~~~
test_function.cpp:13:48: error: 'matrix_row' was not declared in this scope
List output = List::create(_["matrix_row"] = matrix_row);
^~~~~~~~~~
test_function.cpp:13:48: note: suggested alternative: 'sum_matrix_row'
List output = List::create(_["matrix_row"] = matrix_row);
^~~~~~~~~~
sum_matrix_row
make: *** [C:/PROGRA~1/R/R-41~1.0/etc/x64/Makeconf:245: test_function.o] Error 1
ERROR: compilation failed for package 'packagetest'
I found that I write aram instead of arma, now it's fixed!
I have yocto dev environment setup, in which I can bitbake and run a simple c++ application in the target. Now I want to try with simple Qt application. When I execute bitbake-layers show-layers it shows meta-qt5 in the list...
meta-qt5
/home/prc1cob/repo/out/salt/kawa/../../..//os/external/meta-qt5 7
meta-oe
/home/prc1cob/repo/out/salt/kawa/../../../build/yocto/meta-openembedded/meta-oe
6
With this, I assume qt5 is already present in my yocto build.
How to write .bb file to build a simple HelloWorld qt application as below...
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Hello World";
return a.exec();
}
Thankyou!!
Yocto provides a great class qmake5 to compile QT projects based on QMake.
In order to use it create a .pro file for the project:
qtexample.pro
QT += core
SOURCES += qtexample.cpp
qtexample.cpp
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Hello World";
return a.exec();
}
Now, in your layer, you can add a simple recipe that compiles that project.
For example: meta-custom/recipes-project/qtexample
In qtexample folder create files folder and copy qtexample.pro and qtexample.cpp in it.
In qtexample folder directly create qtexample_0.1.bb recipe:
SUMMARY = "QT Example Recipe"
LICENSE = "CLOSED"
SRC_URI = "file://qtexample.pro \
file://qtexample.cpp"
DEPENDS += "qtbase"
RDEPENDS_${PN} += "qtwayland"
S = "${WORKDIR}"
inherit qmake5
You can change the version ofcourse (0.1).
The layout should look like this:
meta-custom/
├── recipes-project/
├── qtexample_0.1.bb
└── files/
├── qtexample.pro
└── qtexample.cpp
Then, bitbake qtexample should work and create a qtexample binary that you can find in ${WORKDIR}
My project looks like this:
.
├── CMakeLists.txt
└── src
├── CMakeLists.txt
├── main.cpp
└── radon
├── main.qml
├── qml.qrc
├── radon.cpp
└── radon.h
The src/CMakeList.txt defines two targets, a library and an executable that links to the library.
# only relevant code shown
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt5 5.15 REQUIRED COMPONENTS Core Quick REQUIRED)
add_library(RadonCore
radon/radon.h
radon/radon.cpp
radon/qml.qrc
)
target_link_libraries(RadonCore
PUBLIC Qt5::Core Qt5::Quick)
add_executable(RadonApp
main.cpp)
target_link_libraries(RadonApp
PRIVATE RadonCore)
The library simply contains the entry point to my application, main.cpp merely calls the entry.
// header
#ifndef RADON_RADON_H
#define RADON_RADON_H
namespace radon{
int radon_entry(int argc, char* argv[]);
}
#endif //RADON_RADON_H
// source
#include "radon.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int radon::radon_entry(int argc, char* argv[])
{
// only relevant code shown
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
engine.load(url);
return QGuiApplication::exec();
}
// main.cpp
#include "radon/radon.h"
int main(int argc, char* argv[])
{
return radon::radon_entry(argc, argv);
}
The idea of making the entry independent of main() is so that I can link the library to tests that have their own main(). However, under the configuration, qt complains about not able to find main.qml
QQmlApplicationEngine failed to load component
qrc:/main.qml: No such file or directory
If I provide a main() inside radon.cpp and built it as an executable, then it works fine. What gives?
The qml.qrc looks like this:
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
The issue is that RadonCore is a static library. In Qt, if a static library uses resources, it must expand the Q_INIT_RESOURCE macro somewhere before first use. For more, see here. Note that the macro cannot be placed inside any namespaces (excluding global namespace). So the solution to this problem would so look like this:
extract radon_entry to the global namespace
place a macro call of Q_INIT_RESOURCE(qml) on the first line of radon_entry
I created a new 'C++ library' project in Qt, which has the following header file:
#include "Test_global.h"
#include <QString>
#include <QTcpServer>
class TESTSHARED_EXPORT Test : QTcpServer
{
Q_OJECT
public:
Test();
~Test();
signals:
void NewMessage(QString);
};
(The implementation file is basically empty.)
When I try to build the object, I get errors:
Test.h:8: error: ISO C++ forbids declaration of ‘Q_OJECT’ with no type
Test.h:10: error: expected ‘;’ before ‘public’
Test.cpp:3: error: definition of implicitly-declared 'Test::Test()'
So it looks like moc isn't processing the file at all. What have I done wrong?
It should be Q_OBJECT, not Q_OJECT.