Skip to content

Cannot register node that takes interface as extra parameter #945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
gvallicrosa opened this issue Mar 5, 2025 · 4 comments
Closed

Cannot register node that takes interface as extra parameter #945

gvallicrosa opened this issue Mar 5, 2025 · 4 comments

Comments

@gvallicrosa
Copy link

Describe the bug

Using release 4.6.2.

I was trying to register a node that depends on an interface in its constructor:

#include <behaviortree_cpp/bt_factory.h>

// interface
struct IMotor {
  virtual void doMove() = 0;
};

// implementation
struct LinearMotor : public IMotor {
  void doMove() override {}
};

// node using interface
class PathFollow : public BT::StatefulActionNode {
 public:
  PathFollow(const std::string& name, const BT::NodeConfig& config, IMotor& motor)
      : BT::StatefulActionNode(name, config), imotor_(motor) {}
  static BT::PortsList providedPorts() { return {}; }
  BT::NodeStatus onStart() override { return BT::NodeStatus::SUCCESS; }
  BT::NodeStatus onRunning() override { return BT::NodeStatus::SUCCESS; }
  void onHalted() override {}

 private:
  IMotor& imotor_;
};

int main() {
  LinearMotor motor;
  BT::BehaviorTreeFactory factory;
  factory.registerNodeType<PathFollow>("PathFollow", motor);
  factory.registerNodeType<PathFollow>("PathFollow", PathFollow::providedPorts(), motor);
  factory.registerNodeType<PathFollow, LinearMotor&>("PathFollow", PathFollow::providedPorts(), motor);
  factory.registerNodeType<PathFollow, IMotor&>("PathFollow", PathFollow::providedPorts(), motor);
}

Trying different ways to register the node, none of them compile. Is there a way to use an interface as an extra argument? Already checked the tutorial 8 and normal examples with values compile without problems.

Here is the compilation output:

In file included from /interface.cc:1:
/usr/local/include/behaviortree_cpp/bt_factory.h: In instantiation of ‘void BT::BehaviorTreeFactory::registerNodeType(const string&, ExtraArgs ...) [with T = PathFollow; ExtraArgs = {LinearMotor}; std::string = std::__cxx11::basic_string<char>]’:
/interface.cc:30:39:   required from here
/usr/local/include/behaviortree_cpp/bt_factory.h:379:21: error: static assertion failed: [registerNode]: since you have a static method providedPorts(),
you MUST add a constructor with signature:
(const std::string&, const NodeConfig&)

  379 |       static_assert(!(has_static_ports_list && !param_constructable),
      |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/include/behaviortree_cpp/bt_factory.h:379:21: note: ‘((!(bool)has_static_ports_list) || ((bool)param_constructable))’ evaluates to false
/usr/local/include/behaviortree_cpp/bt_factory.h: In instantiation of ‘void BT::BehaviorTreeFactory::registerNodeType(const string&, const PortsList&, ExtraArgs ...) [with T = PathFollow; ExtraArgs = {LinearMotor}; std::string = std::__cxx11::basic_string<char>; BT::PortsList = std::unordered_map<std::__cxx11::basic_string<char>, BT::PortInfo>]’:
/interface.cc:31:39:   required from here
/usr/local/include/behaviortree_cpp/bt_factory.h:343:41: error: static assertion failed: [registerNode]: the registered class must have at least one of these two constructors:
  (const std::string&, const NodeConfig&) or (const std::string&)
Check also if the constructor is public!)
  343 |     static_assert(default_constructable || param_constructable,
      |                   ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
/usr/local/include/behaviortree_cpp/bt_factory.h:343:41: note: ‘(((bool)default_constructable) || ((bool)param_constructable))’ evaluates to false
/usr/local/include/behaviortree_cpp/bt_factory.h: In instantiation of ‘void BT::BehaviorTreeFactory::registerNodeType(const string&, const PortsList&, ExtraArgs ...) [with T = PathFollow; ExtraArgs = {IMotor&}; std::string = std::__cxx11::basic_string<char>; BT::PortsList = std::unordered_map<std::__cxx11::basic_string<char>, BT::PortInfo>]’:
/interface.cc:33:48:   required from here
/usr/local/include/behaviortree_cpp/bt_factory.h:349:67: error: cannot allocate an object of abstract type ‘IMotor’
  349 |     registerBuilder(CreateManifest<T>(ID, ports), CreateBuilder<T>(args...));
      |                                                   ~~~~~~~~~~~~~~~~^~~~~~~~~
/interface.cc:4:8: note:   because the following virtual functions are pure within ‘IMotor’:
    4 | struct IMotor {
      |        ^~~~~~
/interface.cc:5:16: note:     ‘virtual void IMotor::doMove()’
    5 |   virtual void doMove() = 0;
      |

This node should be "param_constructable" with the LinearMotor struct passed to it. Any guess on how to do it? Am I missing something basic?

@gvallicrosa gvallicrosa changed the title Cannot register node that takes extra parameters Cannot register node that takes interface as extra parameter Mar 5, 2025
@vincent-hui
Copy link
Contributor

Perhaps you need to make your class PathFollow default_constructable.

@gvallicrosa
Copy link
Author

But then I would have to pass the argument in an init function using a visitor as explained in the tutorial, but I would rather prefer to have everything on a single step. Also avoid pointers if possible,

@gvallicrosa
Copy link
Author

If instead of using references, std::shared_ptr are used, then the code compiles correctly

#include <behaviortree_cpp/bt_factory.h>
#include <iostream>
#include <memory>

// interface
struct IMotor {
  virtual void doMove() = 0;
};

// implementation
struct LinearMotor : public IMotor {
  void doMove() override { std::cout << ">> doMove\n"; }
};

// node using interface
class PathFollow : public BT::StatefulActionNode {
public:
  PathFollow(const std::string &name, const BT::NodeConfig &config,
             std::shared_ptr<IMotor> motor)
      : BT::StatefulActionNode(name, config), imotor_(motor) {}
  static BT::PortsList providedPorts() { return {}; }
  BT::NodeStatus onStart() override {
    std::cout << "onStart\n";
    imotor_->doMove();
    return BT::NodeStatus::RUNNING;
  }
  BT::NodeStatus onRunning() override {
    std::cout << "onRunning\n";
    imotor_->doMove();
    return BT::NodeStatus::SUCCESS;
  }
  void onHalted() override {}

private:
  std::shared_ptr<IMotor> imotor_;
};

int main() {
  auto motor = std::make_shared<LinearMotor>();
  BT::BehaviorTreeFactory factory;
  factory.registerNodeType<PathFollow>("PathFollow", motor);

  auto tree =
      factory.createTreeFromText((" <root BTCPP_format=\"4\">"
                                  "  <BehaviorTree ID=\"MainTree\">"
                                  "    <Sequence name=\"root_sequence\">"
                                  "      <PathFollow name=\"path_follow\"/>"
                                  "    </Sequence>"
                                  "  </BehaviorTree>"
                                  "</root>"));
  tree.tickWhileRunning();
}

@facontidavide
Copy link
Collaborator

This compiles:

LinearMotor motor;
BT::BehaviorTreeFactory factory;
factory.registerNodeType<PathFollow>("PathFollow", std::ref(motor));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants