ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
0

Include methods from another node header (C++)

asked 2020-02-28 04:27:42 -0600

martinandrovich gravatar image

updated 2020-03-02 04:56:55 -0600

I have a node with some methods declared in /include/node_1.h and defined in /src/node_1.cpp. Now, I want to include that header into another node /src/node_2.cpp and use the methods there, like:

// node_2.cpp
#include "pkgname/node_1.h"

// ...
some_method_from_node_1();

I can catkin_make with including, but I get a linker error (undefined reference to the method) when trying to compile while calling the method from node_2.

My CMakeLists.txt for the package looks like:

# ...

## declare a catkin package
catkin_package(
    INCLUDE_DIRS include
    CATKIN_DEPENDS message_runtime
)

## include dirs
include_directories(include ${catkin_INCLUDE_DIRS})

## nodes
set(NODES
    node_1
    node_2
)

foreach(node IN LISTS NODES)

    add_executable(${node} src/${node}.cpp)
    target_link_libraries(${node} ${catkin_LIBRARIES})
    add_dependencies(${node} hric_sys_generate_messages_cpp)

endforeach()

How can I make it work and is this approach even idiomatically correct?

EDIT:

So, I have a method called my_test_func(), which I can call from node_1 but get a linker error if I try to invoke it from node_2, although the include works. This is the (simplified) code:

include/node_1.h:

# pragma once

void my_test_func();

src/node_1.cpp:

#include "package/node_1.h"

void my_test_func()
{
    ROS_INFO("API CALL!");
}

int main()
{
    // ...
    my_test_func(); // WORKS
}

src/node_2.cpp:

#include "package/node_1.h"

int main()
{
    // ...
    my_test_func(); // DOESN'T WORK
}
edit retag flag offensive close merge delete

Comments

So the linker can't find the function. Are you sure that the function is defined correctly and that it can be called. E.g. can you call it in node_1? If you also put your header and source files here, it is more easy to answer your question.

Wilco Bonestroo gravatar image Wilco Bonestroo  ( 2020-02-28 13:48:27 -0600 )edit

It worked from node_1, yes, and I could include the method in node_2. I didn't upload the full code, because I simplified the problem with different names. I think the error might have been making the method definition in node_1 inline. I will check that on Monday.

martinandrovich gravatar image martinandrovich  ( 2020-02-28 14:20:07 -0600 )edit

So, I added some example code below.. It still doesn't work, even if the method is not inlined.

martinandrovich gravatar image martinandrovich  ( 2020-03-02 03:06:00 -0600 )edit

2 Answers

Sort by ยป oldest newest most voted
0

answered 2020-03-03 01:17:56 -0600

Thazz gravatar image

You have a function my_test_func() declared in node_1.h and implemented in node_1.cpp. When you try to use it in node_2.cpp, the linker is missing it's implementation, as only declaration in node_1.h is known, but implementation of that function is missing.

To solve this you have 2 options: - remove implementation of my_test_func() from node_1.cpp and add it to node_1.h file (so that function is declared and implemented in header file) - Create a class Node1 (in node_1.h/cpp) with my_test_func() public member function of that class. Then create an instance of this class in node_2.cpp and call my_test_func() as member of this class, i.e.

#include "package/node_1.h"

int main()
{
    // ...
    Node1 n1;
    n1.my_test_func();
}
edit flag offensive delete link more

Comments

It's the same option in the end, eventhough you create a class the implementation of the function would need to be in the header file to have this working. It's a general good practice not to mix declaration and implementation.

Delb gravatar image Delb  ( 2020-03-03 04:03:57 -0600 )edit

So, defining the method in the header file works, although I don't understand why it doesn't result in a multiple-definitions error. Also, I want to modify some data (e.g. a vector in node_1.cpp) using an interface/function calls from node_2, but maybe ROS is not made for that?

martinandrovich gravatar image martinandrovich  ( 2020-03-03 07:51:46 -0600 )edit

maybe ROS is not made for that?

ROSis a collection of tools, libraries, and conventions (from ROS.org) so you can do whatever you want with ROS using C++ or Python.

It looks like you need to use classes, I would advise you to learn the basics of Object-Oriented Programming and C++ before getting into ROS, wihtout this background using ROS would be very challenging.

Delb gravatar image Delb  ( 2020-03-03 09:47:00 -0600 )edit

I'm basically trying to call node_1's methods from node_2 that will alter some internal data structure of node_1, e.g. node1::add_something(). I am not sure that ROS is designed for that without using messages or services. Also, OOP is not really relevant here; if anything, node_1 is a utility class.

martinandrovich gravatar image martinandrovich  ( 2020-03-03 11:11:51 -0600 )edit

I am not sure that ROS is designed for that without using messages or services

ROS is designed to do this with messages or services.

Now with your additionnal inputs I feel we are diverging from your original question ("include methdods from another header"), if one of the two answers has answered this specific question you can mark it as correct.

For your other question(s) I still don't see what is your blocking point, with our suggestions you still can't call methods of node_1 from node_2 ? I would advise you to open a new question describing your whole problematic in details, telling us what you have tried so far and what your errors/issues are.

Delb gravatar image Delb  ( 2020-03-04 04:09:25 -0600 )edit
1

answered 2020-03-03 03:32:47 -0600

Delb gravatar image

To re-use functions that have been implemented in another cpp file you need to have this file compiled as a library. That's exactly what catkin_make tells you with the error undefined reference to .... Basically, during the compilation all the directives #include get replaced by the content of the included file, so here you end up with node_1 having the declaration and implementation of the function my_test_func in the executable but not for node_2. So when you call my_test_func() in node_2 you don't know its implementation. What you need to do is having node_1.cpp compiled as a library. In that case you will link the library to node_2.cpp that will get the implementation of the function.

Your CMakeLists.txt should be :

# Declare the library node_1
add_library(node_1 src/node_1.cpp)
target_link_libraries(node_1 ${catkin_LIBRARIES})
add_dependencies(node_1 hric_sys_generate_messages_cpp)

# Declare the executable node_2 and link it to node_1
add_executable(node_2 src/node_2.cpp)
target_link_libraries(node_2 ${catkin_LIBRARIES} node_1)
add_dependencies(node_2 hric_sys_generate_messages_cpp)

# Declare the executable node_1_exe if you still want to run node_1
# You don't need to link it to the library node_1 since the implementation
# of the function will be in node_1.cpp
add_executable(node_1_exe src/node_2.cpp)
target_link_libraries(node_1_exe ${catkin_LIBRARIES})
add_dependencies(node_1_exe hric_sys_generate_messages_cpp)
edit flag offensive delete link more

Comments

This is pretty much what I've found out as well.. I think that I will turn node_1 into a separate package and compile it as a library, since I want an interface to it. But can I still run node_1 as a ROS node, even if it's compiled as a library (or something alike)?

martinandrovich gravatar image martinandrovich  ( 2020-03-03 07:44:37 -0600 )edit

can I still run node_1 as a ROS node, even if it's compiled as a library

If it's only compiled as a library then no (meaning only add_library), you can only use rosrun if there is add_executable.

In my example the library node_1 allows you to use the methods from node_1.cpp (thanks to add_library.(..)) if other nodes need it but you can also use rosrun package node_1_exe to run node_1.cpp alone (thanks to add_executable(..)). I admit the naming is bad but in the end node_1 is the library and node_1_exe is the executable, these are two different entities generated from node1.cpp.

Delb gravatar image Delb  ( 2020-03-03 08:13:48 -0600 )edit

I think that I will turn node_1 into a separate package

I don't see the point to that. But I also don't see why you would want to use node1.cpp as a library and an executable too you might want to rethink your design. Maybe we are running into an xy-problem.

Delb gravatar image Delb  ( 2020-03-03 08:16:28 -0600 )edit

You might be correct that it is not the correct design approach; essentially I am creating a data synchronizer that can dynamically register nodes; here node_1 is the main application where node_2 would be some aux node that tries to register itself during run-time, i.e. from node_2 calling: node1::register(args).

I specifically want to use method calls and not ROS messages or services. Or maybe there's some other method that I don't know of.

martinandrovich gravatar image martinandrovich  ( 2020-03-03 11:17:24 -0600 )edit

Question Tools

1 follower

Stats

Asked: 2020-02-28 04:27:42 -0600

Seen: 1,099 times

Last updated: Mar 03 '20