
One thing I asked myself reading the doc, was the handling of const-ness in method signatures, whether it was supported or not, and if it was, consequences on overloading.
`const` is supported. Some examples use it (e.g. https://github.com/jll63/Boost.OpenMethod/blob/master/examples/matrix.cpp ).
Another question I had relates to Steven's Boost.TypeErasure, and similarities / differences with your approach. Naively I'd think there was some overlap, at least for single-dispatch, no?
First a disclaimer: I am not very familiar with Boost.TypeErasure. If I make incorrect claims about it, I apologize in advance, and please do correct me.
The two libraries are obviously in the same space: runtime polymorphism, and a solution to the Expression Problem (thanks to BOOST_TYPE_ERASURE_FREE). There are some major differences though, and I suspect that they can be counted in favor, or against, either library, depending on one's distate for inheritance. I will go over them starting with the more important then down.
As far as I can tell, TypeErasure, Rust traits and Go interfaces don't support open recursion. You are granted one jump through an `any`, and you land in an overrider with a plain reference. You lose polymorphism the first time through the door.
Related to this, objects returned from overriders have no means of carrying the functionality that may have initially existed in the any/trait/interface. Let's look at an example.
#include <iostream> #include < https://jll63.github.io/Boost.OpenMethod/boost/openmethod.hpp>
struct Matrix { virtual ~Matrix() = default; }; struct OrdinaryMatrix : Matrix {}; struct SymmetricMatrix : Matrix {};
using namespace boost::openmethod;
BOOST_OPENMETHOD_CLASSES(Matrix, OrdinaryMatrix, SymmetricMatrix);
BOOST_OPENMETHOD( transpose, (shared_virtual_ptr<Matrix>), shared_virtual_ptr<Matrix>);
BOOST_OPENMETHOD_OVERRIDE( transpose, (shared_virtual_ptr<OrdinaryMatrix>), shared_virtual_ptr<Matrix>) { return make_shared_virtual<OrdinaryMatrix>(); }
BOOST_OPENMETHOD_OVERRIDE( transpose, (shared_virtual_ptr<SymmetricMatrix> m), shared_virtual_ptr<Matrix>) { return m; }
BOOST_OPENMETHOD( to_json, (shared_virtual_ptr<Matrix>), std::string);
BOOST_OPENMETHOD_OVERRIDE( to_json, (shared_virtual_ptr<OrdinaryMatrix> m), std::string) { return "JSON for ordinary matrix"; }
BOOST_OPENMETHOD_OVERRIDE( to_json, (shared_virtual_ptr<SymmetricMatrix> m), std::string) { return "JSON for a symmetric matrix\n"; }
int main() { initialize();
auto m = make_shared_virtual<SymmetricMatrix>(); auto t = transpose(m); std::cout << to_json(t) << "\n";
return 0; }
(https://godbolt.org/z/xG91rfqGr)
Transposition, addition, etc are clearly part of a matrix API, whereas `to_json` is a cross-cutting concern. With open-methods, we can _add_ the "jsonable" functionality to _Matrix_ - even though we don't touch the source.
I guess that a translation of this example to `any` (or Rust) would look like this: have a MatrixType concept that states that matrices can be transposed (and added, etc). We can also add a concept, say, JsonSerializable stating that a Matrix can be converted to a JSON string. Can we make `to_json(transpose(m))` work? Without making MatrixType aware of JsonSerializable?
So this is the major differences, but there are a few others...
OpenMethod embraces inheritance. Some will dislike that. Live and let live...
It looks like creating an `any` always requires a call to `new`. Storage is managed via a `std::shared_ptr`. OpenMethod can work with zero allocations after `initialize` has been called. Later I will introduce local sequential allocators for completely alloc-free operation.
While the syntax of TypeErasure is really nice, I suspect that `any` declarations can become unwieldy in larger programs, in presence of a couple dozens of functions in the interface.
Oh yes, I almost forgot: multiple dispatch indeed :)
J-L