Git Product home page Git Product logo

te's Introduction

[Boost::ext].TE Boost Licence Build Status Coveralls Github Issues Try it online

Your C++17 one header only run-time polymorphism (type erasure) library with no dependencies, macros and limited boilerplate

Quick start

[Boost::ext].TE requires only one file. Get the latest header here!

#include <boost/te.hpp> // Requires C++17 (Tested: GCC-6+, Clang-4.0+, Apple:Clang-9.1+)
namespace te = boost::te;

Erase it

// Define interface of something which is drawable
struct Drawable {
  void draw(std::ostream &out) const {
    te::call([](auto const &self, auto &out) { self.draw(out); }, *this, out);
  }
};

// Define implementation which is drawable (No inheritance)
struct Square {
  void draw(std::ostream &out) const { out << "Square"; }
};

// Define other implementation which is drawable (No inheritance)
struct Circle {
  void draw(std::ostream &out) const { out << "Circle"; }
};

// Define object which can hold drawable objects
void draw(te::poly<Drawable> const &drawable) { drawable.draw(std::cout); }

int main() {
  // Call draw polymorphically (Value semantics / Small Buffer Optimization)
  draw(Circle{}); // prints Circle
  draw(Square{}); // prints Square
}

Alternatively, erase + declare it

struct Drawable : te::poly<Drawable> {
  using te::poly<Drawable>::poly;

  void draw(std::ostream &out) const {
    te::call([](auto const &self, auto &out) { self.draw(out); }, *this, out);
  }
};

void draw(Drawable const &drawable) { drawable.draw(std::cout); }

int main() {
  draw(Circle{}); // prints Circle
  draw(Square{}); // prints Square
}

Store it

int main() {
  std::vector<te::poly<Drawable>> drawables{};

  drawables.push_back(Square{});
  drawables.push_back(Circle{});

  for (const auto &drawable : drawables) {
    drawable.draw(std::cout); // prints Square Circle
  }
}

Overload it

struct Addable {
  constexpr auto add(int i)        { return te::call<int>(add_impl, *this, i); }
  constexpr auto add(int a, int b) { return te::call<int>(add_impl, *this, a, b); }

private:
  static constexpr auto add_impl = [](auto &self, auto... args) {
    return self.add(args...);
  };
};

class Calc {
public:
  constexpr auto add(int i)        { return i; }
  constexpr auto add(int a, int b) { return a + b; }
};

int main() {
  te::poly<Addable> addable{Calc{}};
  assert(3 == addable.add(3));
  assert(3 == addable.add(1, 2));
}

Override it

namespace v1 {
  struct Drawable {
    void draw(std::ostream &out) const {
      te::call([](auto const &self, auto &&... args) { self.draw(args...); }, *this, out, "v1");
    }
  };
} // namespace v1

namespace v2 {
  struct Drawable : v1::Drawable {
    Drawable() { te::extends<v1::Drawable>(*this); }

    // override
    void draw(std::ostream &out) const {
      te::call([](auto const &self, auto &&... args) { self.draw(args...); }, *this, out, "v2");
    }

    // extend/overload
    void draw(std::ostream& out, int minor) const {
      te::call([](auto const &self, auto &&... args) { self.draw(args...); },
        *this, out, "v2." + std::to_string(minor));
    }
  };
} // namespace v2

struct Square {
  void draw(std::ostream &out, const std::string &v) const {
    out << v << "::Square";
  }
};

struct Circle {
  void draw(std::ostream &out, const std::string &v) const {
    out << v << "::Circle";
  }
};

template <class T, class... Ts>
void draw(te::poly<T> const &drawable, const Ts... args) {
  drawable.draw(std::cout, args...);
}

int main() {
  draw<v1::Drawable>(Circle{});    // prints v1::Circle
  draw<v1::Drawable>(Square{});    // prints v1::Square

  draw<v2::Drawable>(Circle{});    // prints v2::Circle
  draw<v2::Drawable>(Circle{}, 1); // prints v2.1::Circle
  draw<v2::Drawable>(Square{});    // prints v2::Square
  draw<v2::Drawable>(Square{}, 2); // prints v2.2::Square
}

Conceptify it (Requires C++20)

struct Drawable {
  void draw(std::ostream &out) const {
    te::call([](auto const &self, auto &out)
      -> decltype(self.draw(out)) { self.draw(out); }, *this, out);
  }
};

struct Square {
  void draw(std::ostream &out) const { out << "Square"; }
};

struct Circle {
  // void draw(std::ostream &out) const { out << "Circle"; }
};

template<te::conceptify<Drawable> TDrawable>
void draw(TDrawable const &drawable) { drawable.draw(std::cout); }

int main() {
  {
    te::var<Drawable> drawable = Square{};
    drawable.draw(std::cout);// prints Square
  }

  {
    // te::var<Drawable> drawable = Square{}; // error: deduced initializer does not
    // drawable.draw(std::cout);              //        satisfy placeholder constraints (draw)
  }

  {
    auto drawable = Square{};
    draw(drawable);  // prints Square
  }

  {
    // auto drawable = Circle{}; // error: constraints not satisifed (draw)
    // draw(drawable);
  }
}

Unify it

void draw(struct Circle const&, std::ostream&);

struct Drawable {
  void draw(std::ostream &out) const {
    te::call(
      [](auto const &self, auto &out) {
        if constexpr(std::experimental::is_detected<drawable_t, decltype(self), decltype(out)>{}) {
          self.draw(out);
        } else {
          ::draw(self, out);
        }
      }, *this, out
    );
  }

 private:
  template<class T, class... Ts>
  using drawable_t = decltype(std::declval<T>().draw(std::declval<Ts>()...));
};

struct Square {
  void draw(std::ostream &out) const { out << "Member Square"; }
};

struct Circle { };

void draw(Circle const&, std::ostream& out) {
  out << "Non-member Circle";
}

void draw(te::poly<Drawable> const &drawable) { drawable.draw(std::cout); }

int main() {
  draw(Circle{});  // prints Non-member Circle
  draw(Square{});  // prints Member Square
}

Forward it

// header
struct Drawable {
  void draw(std::ostream &out) const;
};

void draw(te::poly<Drawable> const &);

struct Square {
  void draw(std::ostream &out) const { out << "Square"; }
};

struct Circle {
  void draw(std::ostream &out) const { out << "Circle"; }
};

// cpp
void draw(te::poly<Drawable> const &drawable) { drawable.draw(std::cout); }

void Drawable::draw(std::ostream& out) const {
  te::call([](auto const &self, auto &out) { self.draw(out); }, *this, out);
}

// uasage
int main() {
  draw(Circle{});  // prints Circle
  draw(Square{});  // prints Square
}

Call it

template <class> struct Function;

template <class R, class... Ts> struct Function<R(Ts...)> {
  constexpr inline auto operator()(Ts... args) {
    return te::call<R>([](auto &self, Ts... args) { return self(args...); }, *this, args...);
  }

  // Option 1 - Explicit requirements for class templates
  // template<class T>
  // auto requires__() -> decltype(&T::operator());
};

// Option 2 - Explicit template instantiation
// template struct Function<int(int)>;

int main() {
  // Option 1 or 2 is required on some compilers
  te::poly<Function<int(int)>> f{[](int i) { return i; }};
  assert(42 == f(42));
}

Customize it

int main() {
  te::poly<Drawable,
           te::local_storage<16>, // te::dynamic_storage (default)
           te::static_vtable      // (default)
  > drawable{Circle{}};
  drawable.draw(std::cout); // prints Circle
}

Macro it?

#define REQUIRES(R, name, ...) R {                             \
  return ::te::call<R>(                                        \
    [](auto&& self, auto&&... args) {                          \
      return self.name(std::forward<decltype(args)>(args)...); \
    }, *this, ## __VA_ARGS__                                   \
  );                                                           \
}

struct Addable {
  auto add(int i) -> REQUIRES(int, add, i);
  auto add(int a, int b) -> REQUIRES(int, add, a, b);
};

class Calc {
 public:
  auto add(int i) { return i; }
  auto add(int a, int b) { return a + b; }
};

int main() {
  te::poly<Addable> addable{Calc{}};
  assert(3 == addable.add(3));
  assert(3 == addable.add(1, 2));
}

Inject it ([Boost::ext].DI)

class Example {
 public:
  Example(Drawable const drawable, std::ostream& out)
    : drawable{drawable}, out{out}
  { }

  void draw() {
    drawable.draw(out);
  }

 private:
  Drawable const drawable;
  std::ostream& out;
};

int main() {
  const auto injector = di::make_injector(
    di::bind<Drawable>.to<Circle>(),
    di::bind<std::ostream>.to(std::cout)
  );

  injector.create<Example>().draw(); // prints Circle
}

Mock it (GUnit)

"should mock drawable object"_test = [] {
  GMock<Drawable> drawable;

  EXPECT_CALL(drawable, draw(std::cout));

  draw(drawable);
};

Similar libraries


Disclaimer [Boost::ext].TE is not an official Boost library.

te's People

Contributors

cstratopoulos avatar endlessarch avatar jimkoen avatar kris-jusiak avatar krzysztof-jusiak avatar pfeatherstone avatar redboltz avatar uknys avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

te's Issues

Looking for a mechanism to make poly comparable, and/or to be able to do runtime casts

Expected Behavior

I expected to be able to find a way to make the poly interface comparable, so one could compare the value stored in it. It's reasonably easy to add a way to check if the types are the same (by adding a function to make them return the type_index of type inside the poly), then return false in the comparison operator when they are not, but I've not been able to find a way to compare the same types by calling the operator== of the right one when those are the same. Example:

#include <iostream>
#include <typeindex>

#include "te.hpp"

using std::cout;
using std::endl;

namespace te = boost::te;

struct One {
     bool operator==(const One&) const {
         return true;
     }
};

struct Two {
     bool operator==(const Two&) const {
         return true;
     }
};


struct Comparable : te::poly<Comparable> {
    using te::poly<Comparable>::poly;

    bool operator==(const Comparable& other) const {
        auto body = [](auto const& self, Comparable const& other) {
            // `self` is the "downcast" type (One/Two), but `other` is
            // Comparable, the interface. We need to figure out how to get the
            // right type.

            // This doesn't really work, as the types are never the same.
            // if constexpr (std::is_same_v<decltype(self), decltype(other)>)
            //     return self == other;
            // return false;

            // This doesn't compile, as One or Two can (and should) only compare
            // with themselves, but `other` is of type Comparable.
            // return self == other;

            const bool sameType = other.type() == std::type_index(typeid(self));

            // Not ideal, as we know for sure that we are not the same when the
            // type is not the same, but we don't know if two objects of the
            // same type are the same. We need to cast `other` to the type of
            // `self`. Now `other` is Comparable, while `self` is the "real"
            // object put into a Comparable (e.g. One, Two).
            // return sameType;

            // The next lines are the further I've been able to get to.
            if (!sameType)
                return false;

            // Attempt to cast `other` to the same type of `self`.
            using SelfType = decltype(self);

            auto innerBody = [] (auto const& innerSelf, const SelfType& innerOther) {
                if constexpr (std::is_same_v<decltype(innerSelf), SelfType>)
                    return false;
                else
                    return innerSelf == innerOther;
            };
            // Crashes. I tried to swap `other` and `self` with respect to the
            // other te::call, so I could get the `Comparable const& other` cast
            // to the right type inside the poly, and use the `operator==` of
            // `One` or `Two`.
            return te::call<bool>(innerBody, other, self);
        };

        return te::call<bool>(body, *this, other);
    }

    std::type_index type() const {
        return te::call<std::type_index>([](auto const& self) {
            return std::type_index(typeid(self));
        }, *this);
    }
};

int main()
{
    Comparable one = One{};
    Comparable two = Two{};
    cout << one.type().name() << endl;
    cout << two.type().name() << endl;
    cout << "one==one " << (one == one) << endl;
    cout << "one==two " << (one == two) << endl;
    cout << "two==two " << (two == two) << endl;
}

Actual Behavior

With the code as in the cample above, it crashes at runtime, and I could not properly understand why.

Alternatively, I would consider that adding something like te::cast<T>, with a similar behavior as any_cast, would make sense. It would allow one to do the usual operations that one can do with std::any and "normal" polymorphism. The AnyAny library seems to provide that functionality (both the cast, and a default way to make the polymorphic value comparable via a built-in API for making it provide the operator).

Steps to Reproduce the Problem

Just trying the example above.

Specifications

  • Version: latest te.hpp (2746584)
  • Platform: Linux
  • Subsystem: GCC or clang

Thank you!

How to use a non const erased type ?

Hello,

I wanted to know if it was possible to use [Boost].TE with types which have no const member functions, couldn't find a way to make it work to pass it by ref.

Here's the code :

#include <iostream>
#include <boost/te.hpp>

namespace te = boost::te;

// Define interface of something which is drawable
struct drawable {
  void draw(std::ostream &out)  {
    te::call([](auto &self, auto &out) { self.draw(out); }, *this, out);
  }
};

// this works 
void draw(te::poly<drawable> drawable) { drawable.draw(std::cout); }

// this doesn't work
// void draw(te::poly<drawable>& drawable) { drawable.draw(std::cout); }

// this doesn't work either
// void draw(te::poly<drawable&> drawable) { drawable.draw(std::cout); }

struct Square {
  int value {0};
  void draw(std::ostream &out) { out << "Square"; out << '\n'; value += 1; }
};

struct Circle {
  void draw(std::ostream &out) { out << "Circle";  out << '\n';}
};

int main() {
    auto x = Circle{};
    auto y = Square{};

    draw(x); // prints Circle
    draw(y); // prints Square

    // should return 1 if it was passed by ref, but as it's passed by value it's 0
    std::cout << y.value << '\n'; 
    
    return 0;
}

Here's the wandbox : https://wandbox.org/permlink/x3Mo2N4toRhCol2H

problem with instantiation of templated method, probably detection problem

Actual Behavior

Problem with FManager::PropertyHolder::emplace_back
This is templated method used to emplace arbitrary items in implementation`s vector
As I see how library does, it is not instantiating things to type_list, mappings, etc - connected with lambda in emplace_back and user types AddToGlobal1_Property, AddToGlobal2_Property.
When it compiles, clang hangs on segmentations, GCC works well (UB??)

Steps to Reproduce the Problem

https://godbolt.org/z/Gq5bKbdes

Please take a look, thanks!

How to return `std::ostream&` from type erased function?

I wrote the following example based on README.md.

#include <iostream>
#include <vector>

#include <boost/te.hpp>

namespace te = boost::te;

struct Drawable : te::poly<Drawable> {
  using te::poly<Drawable>::poly;

  void draw(std::ostream& out) const {
    te::call([](auto const& self, auto& out) { self.draw(out); }, *this, out);
  }
};

struct Square {
  void draw(std::ostream& out) const { out << "Square"; }
};

struct Circle {
  void draw(std::ostream& out) const { out << "Circle"; }
};

int main() {
  std::vector<Drawable> drawables;

  drawables.push_back(Square{});
  drawables.push_back(Circle{});

  for (auto const& drawable : drawables) {
    drawable.draw(std::cout); // prints Square Circle
  }
}

It works fine. https://wandbox.org/permlink/qkfHT6Zg3Mk9qbCS

I tried to modify

void draw(std::ostream& out)

to

std::ostream& draw(std::ostream& out)

Then I got errors.

https://wandbox.org/permlink/YbEudxsv1oVSgmgC

#include <iostream>
#include <vector>

#include <boost/te.hpp>

namespace te = boost::te;

struct Drawable : te::poly<Drawable> {
  using te::poly<Drawable>::poly;

  std::ostream& draw(std::ostream& out) const {
    return te::call<std::ostream&>([](auto const& self, auto& out) { self.draw(out); }, *this, out);
  }
};

struct Square {
  std::ostream& draw(std::ostream& out) const { return out << "Square"; }
};

struct Circle {
  std::ostream& draw(std::ostream& out) const { return out << "Circle"; }
};

int main() {
  std::vector<Drawable> drawables;

  drawables.push_back(Square{});
  drawables.push_back(Circle{});

  for (auto const& drawable : drawables) {
    drawable.draw(std::cout) << std::endl; // prints Square Circle
  }
}
In file included from prog.cc:4:
/opt/wandbox/te/include/boost/te.hpp:247:10: error: call to deleted constructor of 'std::__1::basic_ostream<char>'
  return reinterpret_cast<R (*)(void *, Ts...)>(self.vptr[N - 1])(
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/wandbox/te/include/boost/te.hpp:271:18: note: in instantiation of function template specialization 'boost::te::v1::detail::call_impl<Drawable, 1, std::__1::basic_ostream<char> &, (lambda at prog.cc:12:36), std::__1::basic_ostream<char> &>' requested here
  return detail::call_impl<I>(
                 ^
prog.cc:12:16: note: in instantiation of function template specialization 'boost::te::v1::call<std::__1::basic_ostream<char> &, 0, (lambda at prog.cc:12:36), Drawable, std::__1::basic_ostream<char> &>' requested here
    return te::call<std::ostream&>([](auto const& self, auto& out) { self.draw(out); }, *this, out);
               ^
/opt/wandbox/clang-6.0.1/include/c++/v1/ostream:181:5: note: 'basic_ostream' has been explicitly marked deleted here
    basic_ostream           (const basic_ostream& __rhs) = delete;
    ^
prog.cc:12:12: error: non-const lvalue reference to type 'basic_ostream<...>' cannot bind to a temporary of type 'basic_ostream<...>'
    return te::call<std::ostream&>([](auto const& self, auto& out) { self.draw(out); }, *this, out);
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.

It seems that te tries to copy std::ostream.

If I changed the return value type from std::ostream& to std::ostream* , then the code works as I expected.

https://wandbox.org/permlink/BexYhlEqmbgam6AV

#include <iostream>
#include <vector>

#include <boost/te.hpp>

namespace te = boost::te;

struct Drawable : te::poly<Drawable> {
  using te::poly<Drawable>::poly;

  std::ostream* draw(std::ostream& out) const {
    return te::call<std::ostream*>([](auto const& self, auto& out) { 
      self.draw(out);
      return &out;
    }, *this, out);
  }
};

struct Square {
  std::ostream* draw(std::ostream& out) const { out << "Square"; return &out; }
};

struct Circle {
  std::ostream* draw(std::ostream& out) const { out << "Circle"; return &out; }
};

int main() {
  std::vector<Drawable> drawables;

  drawables.push_back(Square{});
  drawables.push_back(Circle{});

  for (auto const& drawable : drawables) {
    *drawable.draw(std::cout) << std::endl; // prints Square Circle
  }
}

I noticed that in this case, std::reference_wrapper might help.
I updated the reference version as follows:

https://wandbox.org/permlink/wJ5HBSmk2eGiMMZ5

#include <iostream>
#include <vector>

#include <boost/te.hpp>

namespace te = boost::te;

struct Drawable : te::poly<Drawable> {
  using te::poly<Drawable>::poly;

  std::ostream& draw(std::ostream& out) const {
    //              use reference_wrapper here
    return te::call<std::reference_wrapper<std::ostream>>([](auto const& self, auto& out) { self.draw(out); }, *this, out);
  }
};

struct Square {
  std::ostream& draw(std::ostream& out) const { return out << "Square"; }
};

struct Circle {
  std::ostream& draw(std::ostream& out) const { return out << "Circle"; }
};

int main() {
  std::vector<Drawable> drawables;

  drawables.push_back(Square{});
  drawables.push_back(Circle{});

  for (auto const& drawable : drawables) {
    drawable.draw(std::cout) << std::endl; // prints Square Circle
  }
}

I think that std::reference_wrapper version is elegant.

Is this a right way to return reference from te::call ?

Question on void_ptr

Hi there, i am interested in knowing more about your design choices.
Why do you store the deleter and cloner functions in the void_ptr instead of the vtable?
This seems to be an unnecessary additional 2 pointers per poly value...
I would be reallly interested in understanding what I potentially miss if you have some time to answer 😉
Cheers!

Unable to return a const reference on the interface

I expected to be able to return a const reference to a non-copyable object, like std::type_info:

struct Introspectable : te::poly<Introspectable> {
    const std::type_info& type() const {
        return te::call<const std::type_info&>([](auto const& self) {
            return typeid(self);
        }, *this);
    }
};

But I got this compilation errors:

Compile error (GCC 9.4):

In file included from main.cpp:4:
te.hpp: In instantiation of ‘constexpr auto boost::ext::te::v1::detail::call_impl(const boost::ext::te::v1::detail::poly_base&, std::integral_constant<long unsigned int, _Nm>, boost::ext::te::v1::detail::type_list<R>, TExpr, Ts&& ...) [with I = boost_te::Introspectable; long unsigned int N = 1; R = const std::type_info&; TExpr = boost_te::Introspectable::type() const::<lambda(const auto:3&)>; Ts = {}]’:
te.hpp:554:30:   required from ‘constexpr auto boost::ext::te::v1::call(TExpr, const I&, Ts&& ...) [with R = const std::type_info&; long unsigned int N = 0; TExpr = boost_te::Introspectable::type() const::<lambda(const auto:3&)>; I = boost_te::Introspectable; Ts = {}]’
main.cpp:18:17:   required from here
te.hpp:522:44: error: ‘std::type_info::type_info(const std::type_info&)’ is private within this context
  522 |       self.ptr(), std::forward<Ts>(args)...);
      |                                            ^
In file included from /usr/include/c++/9/bits/exception_ptr.h:39,
                 from /usr/include/c++/9/exception:143,
                 from /usr/include/c++/9/ios:39,
                 from /usr/include/c++/9/ostream:38,
                 from /usr/include/c++/9/iostream:39,
                 from main.cpp:1:
/usr/include/c++/9/typeinfo:178:5: note: declared private here
  178 |     type_info(const type_info&);
      |     ^~~~~~~~~
main.cpp: In member function ‘const std::type_info& boost_te::Introspectable::type() const’:
main.cpp:16:47: warning: returning reference to temporary [-Wreturn-local-addr]
   16 |         return te::call<const std::type_info&>([](auto const& self) {
      |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
   17 |             return typeid(self);
      |             ~~~~~~~~~~~~~~~~~~~~               
   18 |         }, *this);
      |         ~~~~~~~~~                              

Possibly related: #13

Thank you!

Compile error (clang 14):

In file included from main.cpp:4:
./te.hpp:521:10: error: calling a private constructor of class 'std::type_info'
  return reinterpret_cast<R (*)(void *, Ts...)>(self.vptr[N - 1])(
         ^
./te.hpp:554:18: note: in instantiation of function template specialization 'boost::te::detail::call_impl<boost_te::Introspectable, 1, const std::type_info &, (lambda at main.cpp:16:48)>' requested here
  return detail::call_impl<I>(
                 ^
main.cpp:16:20: note: in instantiation of function template specialization 'boost::te::call<const std::type_info &, 0, (lambda at main.cpp:16:48), boost_te::Introspectable>' requested here
        return te::call<const std::type_info&>([](auto const& self) {
                   ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/typeinfo:181:5: note: declared private here
    type_info(const type_info&);
    ^
main.cpp:16:16: warning: returning reference to local temporary object [-Wreturn-stack-address]
        return te::call<const std::type_info&>([](auto const& self) {
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning and 1 error generated.
  • Version: latest te.hpp (2746584)
  • Platform: Linux, GCC or clang
  • Subsystem: (?)

Compilation of Test fails on gcc 8.2

Hi, I am using g++ (GCC) 8.2.1 20181011 (Red Hat 8.2.1-4) on Fedora 28
I've tried to compile the library with default parameters on CMake and got the following error.
(I couldn't make the output prettier sorry about that)

include/boost/te.hpp: In instantiation of ‘constexpr boost::te::v1::poly<I, TStorage, TVtable>::poly(T&&, std::index_sequence<Ns ...>) [with T = Circle; T_ = Circle; long unsigned int ...Ns = {}; I = DrawableT<std::__cxx11::basic_stringstream >; TStorage = boost::te::v1::dynamic_storage; TVtable = boost::te::v1::static_vtable; std::index_sequence<Ns ...> = std::integer_sequence]’:

include/boost/te.hpp:223:68: required from ‘constexpr boost::te::v1::poly<I, TStorage, TVtable>::poly(T&&, TRequires) [with T = Circle; TRequires = boost::te::v1::detail::type_list; I = DrawableT<std::__cxx11::basic_stringstream >; TStorage = boost::te::v1::dynamic_storage; TVtable = boost::te::v1::static_vtable]’

include/boost/te.hpp:213:74: required from ‘constexpr boost::te::v1::poly<I, TStorage, TVtable>::poly(T&&) [with T = Circle; = void; I = DrawableT<std::__cxx11::basic_stringstream >; TStorage = boost::te::v1::dynamic_storage; TVtable = boost::te::v1::static_vtable]’

test.cpp:29:61: required from here
include/boost/te.hpp:231:33: error: static assertion failed
static_assert(sizeof...(Ns) > 0);
~~~~~~~~~~~~~~^~~

I've tried to make a minimal test case on godbolt but instead I got this: https://godbolt.org/z/bbxlXl which also fails on 8.2 (fine on 7.x) but gives a slightly different error message.

Since it compiles fine on both gcc 7.x and clang-trunk, I am not sure whether the problem is with the library or either of the compilers

Question: maturity

NOT A BUG

How mature is this library? Is this production ready?
Are there any big differences to the dyno library? I noticed dyno seems to be a bit bigger so was wondering if that's because it handles corner cases, or subtle cases that need special handling. The only differences to dyno I can see are different storage policies for vtable and the fact you can partially inline functions in the vtable and partially put the rest on the heap. So there is more configurability I guess. But if you don't care about that, would you say this library gives you equal functionality to dyno?

Finally, is it possible to port this library to C++14 or would that require substantial more code or the use of big libraries like boost::mpl or something like that?

Compilation of examples failed on gcc 9, 10 and 11

Expected Behavior

Compilation of examples by gcc 9,10,11 is successfuly

Actual Behavior

Compilation is failed by gcc 9.4.0, 10.3.0 and 11.1.0 with following errors:
Non-member CircleMember Square[ 92%] Built target unified
[ 96%] Building CXX object test/CMakeFiles/te.dir/te.cpp.o
In file included from /home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/test/te.cpp:13:
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp: In instantiation of ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&, std::index_sequence<Ns ...>) [with T = Square; T_ = Square; long unsigned int ...Ns = {}; I = DrawableT<std::basic_ostream >; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable; std::index_sequence<Ns ...> = std::integer_sequence]’:
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:463:68: required from ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&, TRequires) [with T = Square; T_ = Square; TRequires = boost::ext::te::v1::detail::type_list; I = DrawableT<std::basic_ostream >; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable]’
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:443:74: required from ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&) [with T = Square; T_ = Square; typename std::enable_if<(! is_same_v<T_, boost::ext::te::v1::poly<I, TStorage, TVtable> >), bool>::type = true; I = DrawableT<std::basic_ostream >; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable]’
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/test/te.cpp:486:56: required from here
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:475:33: error: static assertion failed
475 | static_assert(sizeof...(Ns) > 0);
| ~~~~~~~~~~~~~~^~~
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp: In instantiation of ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&, std::index_sequence<Ns ...>) [with T = Circle; T_ = Circle; long unsigned int ...Ns = {}; I = DrawableT<std::cxx11::basic_stringstream >; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable; std::index_sequence<Ns ...> = std::integer_sequence]’:
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:463:68: required from ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&, TRequires) [with T = Circle; T
= Circle; TRequires = boost::ext::te::v1::detail::type_list; I = DrawableT<std::cxx11::basic_stringstream >; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable]’
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:443:74: required from ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&) [with T = Circle; T
= Circle; typename std::enable_if<(! is_same_v<T
, boost::ext::te::v1::poly<I, TStorage, TVtable> >), bool>::type = true; I = DrawableT<std::cxx11::basic_stringstream >; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable]’
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/test/te.cpp:493:61: required from here
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:475:33: error: static assertion failed
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp: In instantiation of ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&, std::index_sequence<Ns ...>) [with T = <lambda()>::<lambda(int)>; T
= <lambda()>::<lambda(int)>; long unsigned int ...Ns = {}; I = Function<int(int)>; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable; std::index_sequence<Ns ...> = std::integer_sequence]’:
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:463:68: required from ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&, TRequires) [with T = <lambda()>::<lambda(int)>; T
= <lambda()>::<lambda(int)>; TRequires = boost::ext::te::v1::detail::type_list; I = Function<int(int)>; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable]’
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:443:74: required from ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&) [with T = <lambda()>::<lambda(int)>; T
= <lambda()>::<lambda(int)>; typename std::enable_if<(! is_same_v<T_, boost::ext::te::v1::poly<I, TStorage, TVtable> >), bool>::type = true; I = Function<int(int)>; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable]’
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/test/te.cpp:541:59: required from here
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:475:33: error: static assertion failed
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp: In instantiation of ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&, std::index_sequence<Ns ...>) [with T = <lambda()>::<lambda(int, int)>; T_ = <lambda()>::<lambda(int, int)>; long unsigned int ...Ns = {}; I = Function<int(int, int)>; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable; std::index_sequence<Ns ...> = std::integer_sequence]’:
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:463:68: required from ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&, TRequires) [with T = <lambda()>::<lambda(int, int)>; T_ = <lambda()>::<lambda(int, int)>; TRequires = boost::ext::te::v1::detail::type_list; I = Function<int(int, int)>; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable]’
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:443:74: required from ‘constexpr boost::ext::te::v1::poly<I, TStorage, TVtable>::poly(T&&) [with T = <lambda()>::<lambda(int, int)>; T_ = <lambda()>::<lambda(int, int)>; typename std::enable_if<(! is_same_v<T_, boost::ext::te::v1::poly<I, TStorage, TVtable> >), bool>::type = true; I = Function<int(int, int)>; TStorage = boost::ext::te::v1::dynamic_storage; TVtable = boost::ext::te::v1::static_vtable]’
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/test/te.cpp:547:75: required from here
/home/ikobein/rnd/sw-engineering-rnd/boost-te-test/te/include/boost/te.hpp:475:33: error: static assertion failed
make[2]: *** [test/CMakeFiles/te.dir/build.make:76: test/CMakeFiles/te.dir/te.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:505: test/CMakeFiles/te.dir/all] Error 2
make: *** [Makefile:101: all] Error 2

Steps to Reproduce the Problem

Ubuntu 20.4, cmake 3.24.2, gcc 9.4.0, 10.3.0, 11.1.0
0.clone repository

  1. mkdir build: cd build; cmake ..
  2. make

Bug : segmentation fault

Expected Behavior

We expect the following to work.

Actual Behavior

We get a segmentation fault.
It breaks when using local_storage and using move semantics.
The library just moves or swaps the underlying pointers. That won't work. Explicit move constructors MUST be called.
So I believe void_ptr should be upgraded to include:

  using ptr_t = void *;
  using del_t = void (*)(ptr_t);
  using copy_t = ptr_t (*)(ptr_t);
  using move_t = ptr_t(*)(ptr_t);
  
  ptr_t ptr;
  del_t del;
  copy_t copy;
  move_t move;

Steps to Reproduce the Problem

Run the godbolt link.

Specifications

Run on any system and it will break.

Cannot reserve, resize nor emplace_back in std::vector<te::poly<Type>>

It's not possible to change internal or external size of std::vector that stores te::poly objects, nor emplace_back objects into it.

Example with reserving space in the vector:

#include "external/te/include/te.hpp"
#include <vector>

struct Drawable
{
    auto draw() -> REQUIRES(void, draw)
};

int main()
{
    std::vector<te::poly<Drawable>> drawables;
    drawables.reserve(1);
}

Its compilation fails with:

/usr/include/c++/7/type_traits:1162: error: invalid use of incomplete type ‘struct std::is_constructible<te::v1::poly<Drawable>, te::v1::poly<Drawable, te::v1::dynamic_storage, te::v1::static_vtable>&&>’
     struct __is_move_constructible_impl<_Tp, true>
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When we change reserve to resize, we get the same error and additionally this one:

/usr/include/c++/7/bits/stl_construct.h:75: error: no matching function for call to ‘te::v1::poly<Drawable>::poly()’
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When we change the code to test emplace_back with a potentially drawable object:

struct Circle
{
    void draw() {}
};

int main()
{
    std::vector<te::poly<Drawable>> drawables;
    drawables.emplace_back(Circle{});
}

we get the same error as in the reserve case.

Environment
OS: Ubuntu 17.10 x64
Compiler: GCC 7.2 x64

windows msvc2019

Expected Behavior

Actual Behavior

example.cpp
https://raw.githubusercontent.com/boost-experimental/te/master/include/boost/te.hpp(18): warning C4068: unknown pragma 'GCC'
https://raw.githubusercontent.com/boost-experimental/te/master/include/boost/te.hpp(298): error C2062: type 'bool' unexpected
https://raw.githubusercontent.com/boost-experimental/te/master/include/boost/te.hpp(298): error C2143: syntax error: missing ';' before '{'
https://raw.githubusercontent.com/boost-experimental/te/master/include/boost/te.hpp(298): error C2447: '{': missing function header (old-style formal list?)
https://raw.githubusercontent.com/boost-experimental/te/master/include/boost/te.hpp(304): error C2062: type 'bool' unexpected
https://raw.githubusercontent.com/boost-experimental/te/master/include/boost/te.hpp(304): error C2143: syntax error: missing ';' before '{'
https://raw.githubusercontent.com/boost-experimental/te/master/include/boost/te.hpp(304): error C2447: '{': missing function header (old-style formal list?)

(18): error C3203: 'Drawable': unspecialized class template can't be used as a template argument for template parameter 'I', expected a real type (18): error C2039: 'draw': is not a member of 'boost::ext::te::v1::poly' (18): note: see declaration of 'boost::ext::te::v1::poly' (21): error C2641: cannot deduce template arguments for 'Circle' (22): error C2641: cannot deduce template arguments for 'Square' ## Steps to Reproduce the Problem

1.https://godbolt.org/
1.
1.

Specifications

  • Version: x64 msvc 19.latest
  • Platform: windows
  • Subsystem: /std:c++20

Crash when inheriting with local_storage

The following usage of derived te::poly with local_storage ends with a crash instead of printing "Circle":

#include "external/te/include/te.hpp"
#include <iostream>

struct Drawable : te::poly<Drawable, te::local_storage<16>> {
  using te::poly<Drawable, te::local_storage<16>>::poly;

  void draw(std::ostream &out) const {
    te::call([](auto const &self, auto &out) { self.draw(out); }, *this, out);
  }
};

struct Circle {
  void draw(std::ostream &out) const {
    out << "Circle\n";
  }
};

void draw(Drawable const &drawable) { drawable.draw(std::cout); }

int main() {
  draw(Circle{});
}

When we modify Circle to print its copy constructor:

struct Circle {
    Circle() = default;
    Circle(const Circle&) { std::cout << "Circle(const Circle&) ctor\n"; }
  void draw(std::ostream &out) const {
    out << "Circle\n";
  }
};

we get spammed with Circle(const Circle&) ctor in the console until it crashes.

Am I doing something wrong or is there a bug in te?

Environment
OS: Ubuntu 17.10 x64
Compiler: GCC 7.2 x64

Does not compile on certain versions of gcc and clang

This is the example program provided on the main page which showcases better inlining than vanilla inheritance.
https://godbolt.org/z/9h9qf87n5
It fails to compile with certain versions of clang, e.g. 12, 11, 10, 9 and 8. And it fails to compile with certain version of gcc.
I have no idea how to solve this.
I think it has something to do with when templates are "evaluated". The library checks for type completeness and uses requires__ a lot, I imagine to ensure the type is complete at the point where the poly is compiled. Something like that. Maybe for some compilers requires__ isn't enough, so the type is incomplete, so it can't determine the number of methods registered via te::call and mappings::set.
This could either be a compiler bug or the library is abusing a compiler mechanism that isn't guaranteed to work.

the following code (from README, use with boostext::di) does not compile (clang 17, gcc 11)

#include <boost/di.hpp>
#include <boost/te.hpp>
#include <iostream>

namespace te = boost::te;
namespace di = boost::di;

struct Drawable : te::poly<Drawable> {
  using te::poly<Drawable>::poly;

  void draw(std::ostream &out) const {
    te::call([](auto const &self, auto &out) { self.draw(out); }, *this, out);
  }
};

struct Rect {
  void draw(std::ostream &out) const { out << "Rect" << std::endl; }
};

struct Circle {
  void draw(std::ostream &out) const { out << "Circle" << std::endl; }
};

class Example {
public:
  Example(Drawable const drawable, std::ostream &out)
      : drawable{drawable}, out{out} {}

  void draw() { drawable.draw(out); }

private:
  Drawable const drawable;
  std::ostream &out;
};

int main() {
  const auto injector = di::make_injector(di::bind<Drawable>.to<Circle>(),
                                          di::bind<std::ostream>.to(std::cout));

  injector.create<Example>().draw(); // prints Circle
}

Expected Behavior

Actual Behavior

clang error:

poly.cpp:40:12: warning: 'create' is deprecated: creatable constraint not satisfied [-Wdeprecated-declarations]
40 | injector.create().draw(); // prints Circle
| ^
inc/boost/di.hpp:2778:3: note: 'create' has been explicitly marked deprecated here
2778 | create
| ^
poly.cpp:40:12: warning: 'create<Example, 0>' is deprecated: creatable constraint not satisfied [-Wdeprecated-declarations]
40 | injector.create().draw(); // prints Circle
| ^
inc/boost/di.hpp:2775:3: note: 'create<Example, 0>' has been explicitly marked deprecated here
2775 | __BOOST_DI_DEPRECATED("creatable constraint not satisfied")
| ^
inc/boost/di.hpp:33:38: note: expanded from macro '__BOOST_DI_DEPRECATED'
33 | #define _BOOST_DI_DEPRECATED(...) [[deprecated(VA_ARGS)]]
| ^
inc/boost/di.hpp:1241:4: error: inline function 'boost::di::concepts::type::has_to_many_constructor_parameters::max<10>::error' is not defined [-Werror,-Wundefined-inline]
1241 | error(
= "increase BOOST_DI_CFG_CTOR_LIMIT_SIZE value or reduce number of constructor parameters");
| ^
inc/boost/di.hpp:1237:43: note: used here
1237 | return constraint_not_satisfied{}.error();
| ^
2 warnings and 1 error generated.

gcc linking error:

/usr/bin/ld: /tmp/ccU8Y1RZ.o: in function boost::ext::di::v1_3_0::concepts::type<Example>::has_to_many_constructor_parameters::max<10>::operator Example*() const': poly.cpp:(.text._ZNK5boost3ext2di6v1_3_08concepts4typeI7ExampleE34has_to_many_constructor_parameters3maxILi10EEcvPS5_Ev[_ZNK5boost3ext2di6v1_3_08concepts4typeI7ExampleE34has_to_many_constructor_parameters3maxILi10EEcvPS5_Ev]+0x3b): undefined reference to boost::ext::di::v1_3_0::concepts::type::has_to_many_constructor_parameters::max<10>::error(boost::ext::di::v1_3_0::_)'
collect2: error: ld returned 1 exit status

Steps to Reproduce the Problem

Specifications

  • Version:
  • Platform:
  • Subsystem:

No support for move-only types

te doesn't seem to support move-only types, even though in the presence of move constructor and rvalue it will move the object and not use the copy constructor.

For example, given the following code that prints all calls to member functions of a tested class Circle:

#include "external/te/include/te.hpp"
#include <iostream>

using std::cout;

struct Drawable
{
    void draw(std::ostream &out) const
    {
        te::call([](auto const &self, auto &out) { self.draw(out); }, *this, out);
    }
};

struct Circle
{
    Circle() { cout << "Circle()\n"; }
    Circle(const Circle&) { cout << "Circle(const Circle&) ctor\n"; }
    Circle& operator=(const Circle&) { cout << "operator=(const Circle&)\n"; }
    Circle(Circle&&) { cout << "Circle(Circle&&)\n"; }
    Circle& operator=(Circle&&) { cout << "operator=(Circle&&)\n"; }
    ~Circle() { cout << "~Circle()\n"; }

    void draw(std::ostream &out) const { out << "Circle::draw\n"; }
};

void draw(const te::poly<Drawable>& drawable) { drawable.draw(cout); }

int main()
{
    Circle circle{};
    draw(std::move(circle));
}

we will get this output:

Circle()
Circle(Circle&&)
Circle::draw
~Circle()
~Circle()

But when we try to mark the copy constructor and copy assignment operator as deleted as below:

struct Circle
{
    Circle() { cout << "Circle()\n"; }
    Circle(const Circle&) = delete;
    Circle& operator=(const Circle&) = delete;
    Circle(Circle&&) { cout << "Circle(Circle&&)\n"; }
    Circle& operator=(Circle&&) { cout << "operator=(Circle&&)\n"; }
    ~Circle() { cout << "~Circle()\n"; }

    void draw(std::ostream &out) const { out << "Circle::draw\n"; }
};

we will get a compilation error:

main.cpp:33:19: error: invalid initialization of reference of type ‘const te::v1::poly<Drawable>&’ from expression of type ‘std::remove_reference<Circle&>::type {aka Circle}’
     draw(std::move(circle));
          ~~~~~~~~~^~~~~~~~
main.cpp:28:6: note: in passing argument 1 of ‘void draw(const te::v1::poly<Drawable>&)’
 void draw(const te::poly<Drawable>& drawable) { drawable.draw(cout); }
      ^~~~

Are there any plans to support noncopyable types?

Environment
OS: Ubuntu 17.10 x64
Compiler: GCC 7.2 x64

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.