这一段我们在借鉴ros的genmsg包的实现来创建我们的数据定义到.h文件的自动生成机制。但是genmsg是ros中基于python写的包,还依赖gencpp等包。鉴于此,我们分成几步来做这件事。
(1)将ros中的genmsg包及其依赖的包都抽出来,变成一个与ros环境无关的工程gendo,然后在该工程中进行msg文件到.h文件的测试。但是没有使用catkin_make和cmake,而是用的python脚本。
(2)在gendo中编写一个cpp和.h文件,里面定义一个函数。在该函数中创建一个python运行环境,调用genmsg的函数,并将所需要的参数传进去。将cpp和.h打包成cmake包(不仅仅是.so,而是包括了XXXConfig.cmake文件)。
(3)再创建一个cmake工程,里面有一个cpp,包含了一个main(),里面调用前面的得到的package中定义的函数,验证前面编译的包能否通过find_package()找到。
(4)修改前面生成包,并在里面添加一个宏。该宏接收用户输入的msg文件的名称、路径、依赖的msg的名称、路径,然后将这些参数以及其他参数传给gendo中的函数,并调用该函数,生成.h文件。
上述为我们进行改造的思路。其中第一步已经完成了。本文讲述第二步和第三步。第四步后面再探索。
这里,我们先不用gencpp和genmsg包。而是使用了两个更简单的python文件构成的包来验证上述(1)(2)(3)。
实践
两个工程(在Ubuntu下进行的):
(1)cpp_call_python完成cpp封装python工程,然后将cpp打包成cmake包,供其他cmake包使用find_package调用。
(2)my_test,使用find_package找到上述包,并测试使用里面封装的cpp函数(最终调用python脚本)。
1. cpp_call_python工程目录如下:
其中,Test001.py里面调用了Test002.py里面的函数。而a.cpp里面调用了Test001.py里面的函数。我们的目标是把a.cpp连同a.h封装成一个库。aTest.cpp是内部测试函数,验证能否使用a.cpp中的函数。
Test002.py
def Hello():
print("Hello lijie")
Test001.py
import sys
import Test002
sys.path.append('/home/xuec07/code/test/cpp_call_python')
def aaa():
Test002.Hello()
def add(a,b):
return a + b
# def TestDict(dict):
# print(dict)
# dict["Age"] = 17
# return dict
# class Persion:
# def greet(self,greetStr):
# print(greetStr)
a.h
#ifndef CDD18C7C_8F66_477A_A8C6_8CA086F626E9
#define CDD18C7C_8F66_477A_A8C6_8CA086F626E9
void HelloWorld();
#endif /* CDD18C7C_8F66_477A_A8C6_8CA086F626E9 */
a.cpp
#include <iostream>
#include <stdio.h>
#include <Python.h>
#include "a.h"
// using namespace std;
// void HelloWorld();
// // void Add();
// // void TestTranferDict();
// // void TestClass();
// int main()
// {
// std::cout << "Starting Test ..." << std::endl;
// HelloWorld();
// // cout << "Add()--------------------" << endl;
// // Add();
// // cout << "TestDict-----------------" << endl;
// // TestTransferDict(); import sys
// // cout << "TestClass----------------" << endl;
// // TestClass();
// // system("pause");
// return 0;
// }
//调用输出"Hello World"函数
void HelloWorld()
{
// Py_SetPythonHome(L"/usr/include/python3.8");
// Py_SetPythonHome(L"/usr/lib/python3.8");
Py_Initialize(); //使用python之前,要调用Py_Initialize();这个函数进行初始化
PyObject * pModule = NULL; //声明变量
PyObject * pFunc = NULL;
PyRun_SimpleString("import sys");
//PyRun_SimpleString("sys.path.append('./')");
PyRun_SimpleString("sys.path.append('/home/xuec07/code/test/cpp_call_python')");
// PyRun_SimpleString("sys.path.append('/home/xuec07/code/test/cpp_call_python/Test002.py')");
pModule =PyImport_ImportModule("Test001"); //这里是要调用的Python文件名
std::cout << "pModule=" << pModule << std::endl;
if (pModule)
{
pFunc= PyObject_GetAttrString(pModule, "aaa"); //这里是要调用的函数名
std::cout << "pFunc" << pFunc << std::endl;
PyEval_CallObject(pFunc, NULL); //调用函数,NULL表示参数为空
}
else
{
printf("Module fail");
}
Py_Finalize(); //调用Py_Finalize,这个和Py_Initialize相对应的.
}
// //调用Add函数,传两个int型参数
// void Add()
// {
// Py_Initialize();
// PyObject * pModule = NULL;
// PyObject * pFunc = NULL;
// pModule =PyImport_ImportModule("Test001"); //Test001:Python文件名
// pFunc= PyObject_GetAttrString(pModule, "add"); //Add:Python文件中的函数名
// //创建参数:
// PyObject *pArgs = PyTuple_New(2); //函数调用的参数传递均是以元组的形式打包的,2表示参数个数
// PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 5));//0---序号 i表示创建int型变量
// PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 7));//1---序号
// //返回值
// PyObject *pReturn = NULL;
// pReturn = PyEval_CallObject(pFunc, pArgs); //调用函数
// //将返回值转换为int类型
// int result;
// PyArg_Parse(pReturn, "i", &result); //i表示转换成int型变量
// cout << "5+7 = " << result << endl;
// Py_Finalize();
// }
// //参数传递的类型为字典
// void TestTransferDict()
// {
// Py_Initialize();
// PyObject * pModule = NULL;
// PyObject * pFunc = NULL;
// pModule =PyImport_ImportModule("Test001"); //Test001:Python文件名
// pFunc= PyObject_GetAttrString(pModule, "TestDict"); //Add:Python文件中的函数名
// //创建参数:
// PyObject *pArgs = PyTuple_New(1);
// PyObject *pDict = PyDict_New(); //创建字典类型变量
// PyDict_SetItemString(pDict, "Name", Py_BuildValue("s", "WangYao")); //往字典类型变量中填充数据
// PyDict_SetItemString(pDict, "Age", Py_BuildValue("i", 25)); //往字典类型变量中填充数据
// PyTuple_SetItem(pArgs, 0, pDict);//0---序号 将字典类型变量添加到参数元组中
// //返回值
// PyObject *pReturn = NULL;
// pReturn = PyEval_CallObject(pFunc, pArgs); //调用函数
// //处理返回值:
// int size = PyDict_Size(pReturn);
// cout << "返回字典的大小为: " << size << endl;
// PyObject *pNewAge = PyDict_GetItemString(pReturn, "Age");
// int newAge;
// PyArg_Parse(pNewAge, "i", &newAge);
// cout << "True Age: " << newAge << endl;
// Py_Finalize();
// }
// //测试类
// void TestClass()
// {
// Py_Initialize();
// PyObject * pModule = NULL;
// PyObject * pFunc = NULL;
// pModule =PyImport_ImportModule("Test001"); //Test001:Python文件名
// pFunc= PyObject_GetAttrString(pModule, "TestDict"); //Add:Python文件中的函数名
// //获取Person类
// PyObject *pClassPerson = PyObject_GetAttrString(pModule, "Person");
// //创建Person类的实例
// PyObject *pInstancePerson = PyInstance_New(pClassPerson, NULL, NULL);
// //调用方法
// PyObject_CallMethod(pInstancePerson, "greet", "s", "Hello Kitty"); //s表示传递的是字符串,值为"Hello Kitty"
// Py_Finalize();
// }
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(mytest)
add_library(test1 a.cpp a.h)
SET(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/install)
find_package(PythonLibs REQUIRED)
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
include_directories(/usr/include/python3.8)
target_include_directories(test1 PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
set_target_properties(test1 PROPERTIES PUBLIC_HEADER "a.h")
install(TARGETS test1
EXPORT test1-targets
PUBLIC_HEADER DESTINATION include
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
install(EXPORT test1-targets
NAMESPACE mytest::
FILE test1-config.cmake
DESTINATION lib/cmake/test1)
执行编译后,生成libtest1.a在build目录下,而后因为执行install,会将include目录和lib目录移动到外面。而表征我们生成的是cmake包的test1-config.cmake文件则在CMakeFiles\Export\lib\cmake\test1下。
2. my_test工程目录如下:
test.cpp
#include <iostream>
#include <stdio.h>
#include "a.h"
// void Add();
// void TestTranferDict();
// void TestClass();
int main()
{
std::cout << "Starting Test ..." << std::endl;
HelloWorld();
// cout << "Add()--------------------" << endl;
// Add();
// cout << "TestDict-----------------" << endl;
// TestTransferDict(); import sys
// cout << "TestClass----------------" << endl;
// TestClass();
// system("pause");
return 0;
}
CMakeLists.txt
project(myapp)
cmake_minimum_required(VERSION 2.8)
SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "/home/xuec07/code/test/cpp_call_python/install" "/home/xuec07/code/test/cpp_call_python/")
set(PYTHON_INCLUDE_DIRS "/usr/include/python3.8")
set(PYTHON_LIBRARIES "/usr/lib/x86_64-linux-gnu/libpython3.8.so")
find_package(test1 REQUIRED)
if(test1_FOUND)
message(STATUS "test path : ${test1_INCLUDE_DIRS}")
else()
message(STATUS "test not found!")
endif()
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
find_package(PythonLibs REQUIRED)
include_directories(${PYTHON_INCLUDE_DIRS})
add_executable(${PROJECT_NAME} test.cpp)
target_link_libraries(${PROJECT_NAME} mytest::test1 ${PYTHON_LIBRARIES})
编译后生成myapp可执行程序。