The documentation for Boost.TypeErasure includes an example "polymorphic range formatter" which simulates the concept of a "pure virtual template member function". I am able to compile and run that example code, which defines a class hierarchy that allows a sequence to be formatted in several different ways. I would like to pare it down to a much simpler example which simply accepts a value and sends it to std::out. Here is my naive attempt:
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/iterator.hpp>
#include <boost/type_erasure/operators.hpp>
#include <boost/type_erasure/tuple.hpp>
#include <boost/type_erasure/same_type.hpp>
#include <iostream>
using namespace boost::type_erasure;
struct _x : placeholder {};
class abstract_printer2 {
public:
template<class X>
void print(X x) const {
tuple<requirements, _x> args(x);
do_print(get<0>(args));
}
virtual ~abstract_printer2() {}
protected:
typedef boost::mpl::vector<
ostreamable<std::ostream, _x>
> requirements;
typedef boost::type_erasure::any<requirements, _x> x_type;
virtual void do_print(x_type x) const = 0;
};
class separator_printer2 : public abstract_printer2 {
protected:
virtual void do_print(x_type x) const {
std::cout << x << std::endl;
}
};
int main() {
separator_printer2 p4;
int q = 5;
p4.print(q);
}
When I try to compile that code in Visual Studio 2022, the build fails with:
Build started at 14:02...
1>------ Build started: Project: test, Configuration: Debug x64 ------
1>test.cpp
1>C:\bob\projects\test\test.cpp(17,17): error C2280: 'boost::type_erasure::any<abstract_printer2::requirements,_x>::any(const boost::type_erasure::any<abstract_printer2::requirements,_x> &)': attempting to reference a deleted function
1>C:\bob\projects\boost\boost\type_erasure\any.hpp(1928,1):
1>compiler has generated 'boost::type_erasure::any<abstract_printer2::requirements,_x>::any' here
1>C:\bob\projects\boost\boost\type_erasure\any.hpp(1928,1):
1>'boost::type_erasure::any<abstract_printer2::requirements,_x>::any(const boost::type_erasure::any<abstract_printer2::requirements,_x> &)': function was implicitly deleted because a base class invokes a deleted or inaccessible function 'boost::type_erasure::any_constructor_control<boost::type_erasure::any_constructor_impl<Concept,T>,void>::any_constructor_control(const boost::type_erasure::any_constructor_control<boost::type_erasure::any_constructor_impl<Concept,T>,void> &)'
1> with
1> [
1> Concept=abstract_printer2::requirements,
1> T=_x
1> ]
1>C:\bob\projects\boost\boost\type_erasure\any.hpp(376,5):
1>'boost::type_erasure::any_constructor_control<boost::type_erasure::any_constructor_impl<Concept,T>,void>::any_constructor_control(const boost::type_erasure::any_constructor_control<boost::type_erasure::any_constructor_impl<Concept,T>,void> &)': function was explicitly deleted
1> with
1> [
1> Concept=abstract_printer2::requirements,
1> T=_x
1> ]
1>C:\bob\projects\test\test.cpp(17,17):
1>the template instantiation context (the oldest one first) is
1> C:\bob\projects\test\test.06.factory\test.cpp(38,7):
1> see reference to function template instantiation 'void abstract_printer2::print<int>(X) const' being compiled
1> with
1> [
1> X=int
1> ]
1>Done building project "test.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
========== Build completed at 14:02 and took 01.517 seconds ==========
What am I doing wrong?
Your x_type
is a type that supports ostreamable
.
You did not require copyable. You then tried to copy your x_type
; this copy constructor is deleted.
template<class X>
void print(X x) const {
tuple<requirements, _x> args(x); // <--- tuple of erased elements
do_print(get<0>(args)); // <--- copy first element of tuple (error)
}
You need to either add "copy" to your type erasure requirements, or avoid copying.
Once you do that, you get another error; you are destroying x_type
without requiring it be destructible.
template<class X>
void print(X x) const {
do_print(x_type(std::move(x))); // <-- avoids copying it
}
virtual ~abstract_printer2() {}
protected:
typedef boost::mpl::vector<
ostreamable<std::ostream, _x>,
destructible<_x> // <- added "can destroy it"
> requirements;
and there we go.
A common thing I like using is a reference type instead of a value owning type. This gets rid of the destructable issue:
class abstract_printer2 {
public:
template<class X>
void print(X x) const {
do_print(x_ref(x)); // <-- no move move
}
virtual ~abstract_printer2() {}
protected:
typedef boost::mpl::vector<
ostreamable<std::ostream, _x>
> requirements;
typedef boost::type_erasure::any<requirements, _x&> x_ref; // <-- notice the & and renaming
virtual void do_print(x_ref x) const = 0;
};
as if we don't plan on storing the objects, taking by reference works.
You can also use the alias any_ref<requirements, _x>
instead of any<requirements, _x&>
.