Skip to content

How to use with templated classes? #67

Closed

Description

@jagot

Thank you for this interesting library!

I am trying to reproduce the example in the documentation, but with a templated type hierarchy.

Without templates

First, my version of the example without templates:

#include <iostream>
#include <memory>

#include <boost/openmethod.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>

using boost::openmethod::virtual_ptr;
using namespace boost::openmethod::aliases;

struct Node {};

struct Variable : Node {
  int v;
  Variable(int v) : v(v) {}
};

struct Plus : Node {
  shared_virtual_ptr<Node> left, right;
  Plus(shared_virtual_ptr<Node> left, shared_virtual_ptr<Node>right)
    : left(left), right(right) {}
};

struct Times : Node {
  shared_virtual_ptr<Node> left, right;
  Times(shared_virtual_ptr<Node> left, shared_virtual_ptr<Node>right)
    : left(left), right(right) {}
};


BOOST_OPENMETHOD(value, (virtual_ptr<const Node> node), int);

BOOST_OPENMETHOD_OVERRIDE(value, (virtual_ptr<const Variable> var), int) {
  return var->v;
}

BOOST_OPENMETHOD_OVERRIDE(value, (virtual_ptr<const Plus> plus), int) {
  return value(plus->left) + value(plus->right);
}

BOOST_OPENMETHOD_OVERRIDE(value, (virtual_ptr<const Times> times), int) {
  return value(times->left) * value(times->right);
}

BOOST_OPENMETHOD_CLASSES(Node, Variable, Plus, Times);

int main(int argc, char* argv[]) {
  boost::openmethod::initialize();

  shared_virtual_ptr<Node> a = make_shared_virtual<Variable>(2),
    b = make_shared_virtual<Variable>(3),
    c = make_shared_virtual<Variable>(4),
    d = make_shared_virtual<Plus>(a, b),
    e = make_shared_virtual<Times>(d, c);


  std::cout << "a = " << value(a) << std::endl;
  std::cout << "b = " << value(b) << std::endl;
  std::cout << "c = " << value(c) << std::endl;

  std::cout << "d = " << value(d) << std::endl;
  std::cout << "e = " << value(e) << std::endl;

  return 0;
}

This outputs

a = 2
b = 3
c = 4
d = 5
e = 20

as expected.

With templates

#include <iostream>
#include <memory>

#include <boost/openmethod.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include <boost/openmethod/macros.hpp>

using boost::openmethod::method;
using boost::openmethod::use_classes;
using boost::openmethod::virtual_ptr;
using namespace boost::openmethod::aliases;

template<typename T>
struct Node {};

template<typename T>
struct Variable : Node<T> {
  T v;
  Variable(T v) : v(v) {}
};

template<typename T>
struct Plus : Node<T> {
  shared_virtual_ptr<Node<T>> left, right;
  Plus(shared_virtual_ptr<Node<T>> left, shared_virtual_ptr<Node<T>>right)
    : left(left), right(right) {}
};

template<typename T>
struct Times : Node<T> {
  shared_virtual_ptr<Node<T>> left, right;
  Times(shared_virtual_ptr<Node<T>> left, shared_virtual_ptr<Node<T>>right)
    : left(left), right(right) {}
};

struct BOOST_OPENMETHOD_ID(value);

template<typename T>
using value = method<BOOST_OPENMETHOD_ID(value),
                     T(virtual_ptr<const Node<T>> node)>;

template<typename T>
T variable_value(virtual_ptr<const Variable<T>> var) {
  return var->v;
}

template<typename T>
T plus_value(virtual_ptr<const Plus<T>> plus) {
  return value(plus->left) + value(plus->right);
}

template<typename T>
T times_value(virtual_ptr<const Times<T>> times) {
  return value(times->left) * value(times->right);
}

// Compile fails with "error: expected unqualified-id"
template<typename T>
static value<T>::override<variable_value<T>> override_variable_value;
template<typename T>
static value<T>::override<plus_value<T>> override_plus_value;
template<typename T>
static value<T>::override<times_value<T>> override_times_value;

#define NODE_IMPL(T, NAME) \
use_classes<Node<T>, Variable<T>, Plus<T>, Times<T>> NAME;

NODE_IMPL(float, float_node_classes);

int main(int argc, char* argv[]) {
  boost::openmethod::initialize();

  shared_virtual_ptr<Node<float>> a = make_shared_virtual<Variable<float>>(2),
    b = make_shared_virtual<Variable<float>>(3),
    c = make_shared_virtual<Variable<float>>(4),
    d = make_shared_virtual<Plus<float>>(a, b),
    e = make_shared_virtual<Times<float>>(d, c);

  std::cout << "a = " << value<float>::fn(a) << std::endl;
  std::cout << "b = " << value<float>::fn(b) << std::endl;
  std::cout << "c = " << value<float>::fn(c) << std::endl;

  std::cout << "d = " << value<float>::fn(d) << std::endl;
  std::cout << "e = " << value<float>::fn(e) << std::endl;

  return 0;
}

This does not compile, as indicated by the comment in the code:

/usr/bin/c++ -DBOOST_CONTAINER_NO_LIB -DBOOST_JSON_NO_LIB -DFMT_SHARED -isystem /opt/homebrew/include -std=c++2b -arch arm64 -fcolor-diagnostics -Werror=return-type -Werror=switch -MD -MT CMakeFiles/templated.dir/templated.cpp.o -MF CMakeFiles/templated.dir/templated.cpp.o.d -o CMakeFiles/templated.dir/templated.cpp.o -c /Users/jagot/work/projects/examples/boost-openmethods/templated.cpp
/Users/jagot/work/projects/examples/boost-openmethods/templated.cpp:49:26: error: expected unqualified-id
   49 | static value<T>::override<variable_value<T>> override_variable_value;
      |                          ^
/Users/jagot/work/projects/examples/boost-openmethods/templated.cpp:57:26: error: expected unqualified-id
   57 | static value<T>::override<plus_value<T>> override_plus_value;
      |                          ^
/Users/jagot/work/projects/examples/boost-openmethods/templated.cpp:65:26: error: expected unqualified-id
   65 | static value<T>::override<times_value<T>> override_times_value;
      |                          ^
3 errors generated.

What I am doing wrong? Is it possible to accomplish the same less verbosely, i.e. using the macros, and such that value is a callable function, so I don't have to call using value<T>::fn?

Side note

My code is already written using lots of shared_ptrs. Is it possible to somehow call an open method using shared pointers directly, without the intermediate conversion to shared_virtual_ptr?

In the documentation found here, make_unique_virtual is written as make_unique_virtual_ptr, and similarly for make_shared_virtual. Luckily, my compiler told me to use the correct method.

Activity

  1. self-assigned this
    on Apr 8, 2026
  2. jll63 commented on Apr 8, 2026

    @jll63
    Collaborator

    Thank you for this interesting library!

    Thanks! And sorry for the belated reply, I missed the issue. Gotta set some
    email rules...

    There are a couple of issues here.

    template<typename T>
    using value = method<BOOST_OPENMETHOD_ID(value),
                         T(virtual_ptr<const Node<T>> node)>;
    
    template<typename T>
    T plus_value(virtual_ptr<const Plus<T>> plus) {
      return value(plus->left) + value(plus->right);
    }

    value is a class template and you are calling it as a function. The correct syntax is:

    template<typename T>
    T plus_value(virtual_ptr<const Plus<T>> plus) {
      return value<T>::fn(plus->left) + value<T>::fn(plus->right);
    }

    Secondly, this is wrong in several ways:

    template<typename T>
    static value<T>::override<variable_value<T>> override_variable_value;

    static should not be there. On the other hand, you need to tell the compiler
    that override, dependent on T, is a template, and that the result of the
    instantiation is a type:

    template<typename T>
    typename value<T>::template override<variable_value<T>> override_variable_value;

    Note that, at this point, no variable_value is registered, we just created a
    variable template.

    In this situation, I would go this way:

    template<class T>
    using node_impl = std::tuple<
        use_classes<Node<T>, Variable<T>, Plus<T>, Times<T>>,
        typename value<T>::template override<
            variable_value<T>,
            plus_value<T>,
            times_value<T>
        >
    >;
    
    node_impl<float> node_impl_float;

    Godbolt: https://godbolt.org/z/7rs4dor59

    My code is already written using lots of shared_ptrs. Is it possible to
    somehow call an open method using shared pointers directly, without the
    intermediate conversion to shared_virtual_ptr?

    You could. There is an implicit conversion from a reference to an object to a virtual_ptr, but that constructor requires the type to be polymorphic.

    template<typename T>
    struct Node {virtual ~Node() = default;};
    
    // ...
    
    int main(int argc, char* argv[]) {
      boost::openmethod::initialize();
    
      std::shared_ptr<Node<float>> a = std::make_shared<Variable<float>>(2);
      std::cout << "a = " << value<float>::fn(*a) << std::endl;
    
      return 0;
    }

    See, make_virtual_shared knows the exact type of the object, so it does not
    need to find it at runtime, which requires polymorphic types. It is also faster.

    In the documentation found here, make_unique_virtual is written as
    make_unique_virtual_ptr, and similarly for make_shared_virtual. Luckily, my
    compiler told me to use the correct method.

    Corrected in 1.91 ;-)

  3. locked and limited conversation to collaborators on Apr 8, 2026
  4. converted this issue into a discussion #69 on Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions