|
I've been quite disillusioned with C++ myself as of late. I am currently writing a wrapper class around shared pointers to objects with inheritance (which embed their own private implementations, which also utilize inheritance) so that I can pass them by value and use operator. for method chaining. The SFINAE used for construction from base objects to these shared pointer wrappers is something out of the ninth circle of hell. template<typename T> struct Shared : std::shared_ptr<T> {
template<typename U> struct is_compatible {
typedef char yes[1], no[0];
template<typename V> static yes& test1(typename std::enable_if<std::is_base_of<std::shared_ptr<T>, V>::value>::type*);
template<typename V> static no& test1(...);
template<typename V> static yes& test2(typename std::enable_if<std::is_base_of<element_type, typename V::element_type>::value>::type*);
template<typename V> static no& test2(...);
static constexpr bool value = sizeof(test1<U>(0)) == sizeof(yes) || sizeof(test2<U>(0)) == sizeof(yes);
};
And from the function binder: //value = true if R L::operator()(P...) exists
template<typename L> struct compatible {
template<typename T> static constexpr typename std::is_same<R, decltype(std::declval<T>().operator()(std::declval<P>()...))>::type exists(T*);
template<typename T> static constexpr std::false_type exists(...);
static constexpr bool value = decltype(exists<L>(0))::value;
};
template<typename L> function(const L& object, typename std::enable_if<compatible<L>::value>::type* = nullptr) { callback = new lambda<L>(object); }
And yet ... when actually using the library, it is a thing of beauty. struct TextEditor : Window {
MenuBar menuBar = {this};
Menu menuFile = {&menuBar, "File"};
MenuItem menuQuit = {&menuFile, "Quit"};
VerticalLayout layout = {this};
TextEdit editor = {&layout, Size{~0, ~0}};
TextEditor() {
StatusBar statusBar{this};
statusBar.setFont(Font::sans(8, "Bold")).setText("Line 1, Column 1");
HorizontalLayout findBar(&layout);
findBar.append(Label(&findBar, "Find:"));
findBar.append(LineEdit(&findBar).setBackgroundColor(Color::Yellow)
.onChange([&] { searchFor(text()); }));
findBar.append(Button(&findBar, "Clear")
.onActivate([&] { findBar.widget(1).setText(""); }));
menuQuit.onActivate(&Application::quit);
edit.onChange([&] { updateStatusBar(); });
}
};
There is no need for any memory management, or any usage of pointers. We build UIs, and everything gets automatically released safely when nothing is referring to it anymore. We can declare named objects that we can use later, or we can create dynamic objects and pop them right inside of other objects. We can destroy and unparent things whenever we want. And it's deterministic, reference-counted GC. No pauses for a tracer. No dynamic typing anywhere, all errors are at compile-time.I highly suspect that C++ is unreasonably complicated and that all of this rvalue-reference, variadic template, meta-programming, dynamic-casting polymorphism, is all just voodoo that isn't applicable to general programming. And yet, being able to do it gives me amazing expressive power to write awesome libraries that I could never hope to accomplish in another language. Until I find a language that's even in the same ballpark as C++ in terms of performance, and offers similar expressiveness, it really doesn't even matter how bad C++ can be for library authors. There's no other viable option right now. D is the closest we have, but its complexity already rivals, if not exceeds, that of C++. |
Edit: noticed you're using these in another place (`compatible` implementation), so perhaps there's a reason for a different approach?