C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Fwd: Extension to runtime polymorphism proposed

From: Sebastian Wittmeier <wittmeier_at_[hidden]>
Date: Fri, 10 Apr 2026 04:58:37 +0200
So in other words there probably won't be any performance gains by the proposal as it has to cook with water, too, and the same data layout (whatever is optimal) can be done with a library solution.   Now will there be any usability improvements?   If with reflection the common interface between the types is extracted, there probably won't be any usability improvement by the proposal on top of that either.   -----Ursprüngliche Nachricht----- Von:Muneem via Std-Proposals <std-proposals_at_[hidden]> Gesendet:Fr 10.04.2026 04:44 Betreff:Re: [std-proposals] Fwd: Extension to runtime polymorphism proposed An:std-proposals_at_[hidden]; CC:Muneem <itfllow123_at_[hidden]>; All I am trying to say is that the implementation can do a better job at knowing what to put in every single heterogeneous list element and in the list as a whole. We can try to decide that on our own but that's again abstraction getting leaked all over the ussr, like my code clearly shows what kind of mess that results in and Mr.Bjorn's code was even more painful to watch as it was further constraints by the ABI boundaries of variants when trying to return an element. A new construct bypasses all existing ABI's and will just have its own thing for this one purpose: subscripting a heterogeneous list. As the goated Bjarne Stroustrup said: What you don’t use, you don’t pay for, and What you do use, you couldn’t hand-code any better.  This is why I am pushing for horizontal growth in c++ by pushing for totally new features because the sky has a limit and my code clearly shows it!   On Fri, 10 Apr 2026, 7:26 am Sebastian Wittmeier via Std-Proposals, <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> > wrote: I probably have missed your concept of a layout. Can you point to the mail?   Is flat maps an example for differences between implementations or does your types contain a (flat) map?   Do you say that your type does allow the implementation to use non-owning pointers to the tuple, pointers to the element, copy of the element, copy of the tuple and vtable-like structures, whatever it thinks most perfornant for the list of types and possible operations?   In some cases (and especially if the way of passing within the type is not known) the programmer has to make sure that the initial list is not changed during a function call.   Everything you can cram into the type as template parameter is free for book keeping. Everything else you want to keep you have to use memory and pass at runtime.   You brought up the selected type as bookkeeping data:  - with my Dispatch class the index showed the type  - with std::variant the type is kept in the class, too. That is why std::variant is type-safe and C unions are not  - Just make the type read only (except when it is created). I don't understand, why you need so much book-keeping. Or is it to avoid branches? Then perhaps just rely on the optimizer instead of a fixed bookkeeping inside the type? Except if it is simple enough.     -----Ursprüngliche Nachricht----- Von:Muneem via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> > Gesendet:Fr 10.04.2026 03:14 Betreff:Re: [std-proposals] Fwd: Extension to runtime polymorphism proposed An:std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> ; CC:Muneem <itfllow123_at_[hidden] <mailto:itfllow123_at_[hidden]> >; You aren't getting the point, you do need booking keeping information in order to not be at the mercy of the optimizer. I didn't give the class layout because there is no construct like that yet. What I gave before was an example of what it can look like *in concept* in memory. Again, why not make a flat maps instead of relying on maps or building your own wrappers around tuples? Even if I was wrong about ABI differing about each processor which technically I am not because you can't link code for amd64 with the code for some ARM processor, but  even then a flat maps is fundamentally faster than a normal map. Now you may argue that why not implement it in the library but the issue is that there are no constructs that you can arbitrarly fix to work a certain way because code Already relies on it. On Fri, 10 Apr 2026, 1:00 am Sebastian Wittmeier via Std-Proposals, <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> > wrote: Within the same standard library ABI (= architecture ABI + standard library version) you typically have the same class layout. So you cannot just have a different class layout for each processor out there.   Your argument (or one of your arguments) is that your new feature allows for better performance, and having less book keeping overhead.   How should we understand that argument, if you don't even give an idea how the class layout should or could look like?   If the class layout is the same as existing types, then the question is, why it should be faster at all.   Important aspects are how short the class layout is (object size) and how easy it is to access for typical operations (number of accesses needed, memory indirections, and if you want cache lines). You don't have to give a definition for all possible layouts, just an example for understanding the difference. (You don't actually *have* to do anything, but it would help the discussion.)       Existing building blocks have very simple class layouts:   std::tuple is one element after the other, with some padding for correct alignment. (in some standard libraries in reverse order of elements) std::variant is like a union, each possible value type stored in the same buffer together with an index, signifying which type it has.   Why would your class layouts be so complicated that they cannot be described or reasoned about?   We (or at least me) have to understand, where the performance speedup is coming from. Not from some philosophical musings, but from a direct description.     -----Ursprüngliche Nachricht----- Von:Muneem via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> > Gesendet:Do 09.04.2026 19:53 Betreff:Re: [std-proposals] Fwd: Extension to runtime polymorphism proposed An:std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> ; CC:Muneem <itfllow123_at_[hidden] <mailto:itfllow123_at_[hidden]> >; In short answer: The programmers dosent know what book keeping actually works for that specific hardware.   Detailed answer:  1. The programmers dosent know what book keeping actually works for that specific hardware. Programs compiled for extremely old legacy computer's don't have that much space to spare to get a cache Friendly design. Not to mention that there are many totally different computer that a programmer might not have even heard of, like the intel Xeon CPUs.  2. As shown in my example, I don't know if the N space overhead is better or should I opt to a recursive N-1 call to a base class that handles the rest while inheriting from that. Why? Because I don't know if the optimizer can optimize such recursion it out breaking ABI's or something, like I don't know that for every computer in the world and don't want to.  3. Dont rely on speed that is only there under "some circumstances". 4. This brings me back to writing Painful C code. Like the reason that I or anyone else moves from C to c++ is because C forces me to rewrite flat maps and what not. Even then my implementation is outperformed by the STL because I can't use inline assembly without spending a year learning each target architecture. A implementation like MSCV or Clang is. On Thu, 9 Apr 2026, 9:24 pm Sebastian Wittmeier via Std-Proposals, <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> > wrote: About book keeping information: 1) You can have book keeping data, which is set at compile-time and the compiler chooses the optimal representation (e.g. templates) 2) Other book keeping data exists at runtime and is passed as parameters or even stored in the object 2a) Of that some can under certain circumstances (no ABI boundary) be removed again by the optimizer     -----Ursprüngliche Nachricht----- Von:Muneem via Std-Proposals <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> > Gesendet:Do 09.04.2026 14:55 Betreff:Re: [std-proposals] Fwd: Extension to runtime polymorphism proposed An:std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> ; CC:Muneem <itfllow123_at_[hidden] <mailto:itfllow123_at_[hidden]> >; That's the whole point, tuple dosent keep any book keeping data, it has always been a fun little wierd variadic template struct inheriting from itself, but you don't want that for book keeping Information, and you can't change that without breaking ABI's. To truly optimize without breaking ABI's you need newer mechanics, like you can't optimize a std::list without introducing std::vector. ****Some philosophical analogy(sorry if it's too abstract and personal)**** About the Mr.herb, my answer is that languages change and they must, and we should not allow a few people to force us to build vertically. Personally, I have roots in Afghanistan and Soviets always tried to completely eradicate villages because they saw them as horizontal resistance that keeps on growing exponentially in every direction. The same guys, loved, protected, and heavily developed Afghan urban centers and infrustructure. At the end the horizontal resistance won. The point is that if you have a language that's not willing to grow out of it's pre existing philosophies someone will replace it. Look at C, C++ replaced C. Nothing lives for ever unless it gives rebirth to something else, c++ has been doing it for years, but is stopping now.The main reason for this proposal was that you can't fix everything with metaprogramming techniques, like the sky has a limit unlike what most say: ***How my *container would feel like* ***: #include <type_traits> #include <string> #include <utility>  struct false_t {}; struct true_t {};  template<typename T, typename... Tail> struct Is_t_in_set;  template<typename T, typename Head_t, typename... Tail> struct Is_t_in_set<T, Head_t, Tail...> {     using boolean = std::conditional_t<         std::is_same_v<T, Head_t>,         true_t,         typename Is_t_in_set<T, Tail...>::boolean     >; };  template<typename T, typename Head_t> struct Is_t_in_set<T, Head_t> {     using boolean = std::conditional_t<         std::is_same_v<T, Head_t>, true_t, false_t     >; };  struct empty_t {};  template<typename T> struct Alias_type {     using Type = T; };  template<typename T> using Alias_type_t = typename Alias_type<T>::Type;  template<typename... Args> struct N_type_unique_set;  template<typename Head_t, typename... Tail>  struct N_type_unique_set<Head_t, Tail...>     : std::conditional_t<         std::is_same_v<typename Is_t_in_set<Head_t, Tail...>::boolean, true_t>,          empty_t,          Alias_type<Head_t>     >, N_type_unique_set<Tail...>  {};  template<typename Head_t>  struct N_type_unique_set<Head_t> : Alias_type<Head_t> {};  template <std::size_t N, typename... _Types> struct Element_t;  template <std::size_t N, typename Head_t, typename... Tail> struct Element_t<N, Head_t, Tail...> {     std::size_t tag;     union {         struct {             Head_t* reference;         } head;         Element_t<N - 1, Tail...> tail;     };      inline Element_t(Head_t& a) : tag{N} {          head.reference = &a;      }      template<typename T>     inline Element_t(T&& a) : tail{ std::forward<T>(a) } {          tag = tail.tag;      }      template<typename Func_t, typename T>     inline void dispatch(T&& a) {         if (tag == N) {             Func_t{}(*head.reference, std::forward<T>(a));         } else {             tail.template dispatch<Func_t>(std::forward<T>(a));         }     }      inline Head_t& operator=(Head_t& a) {         if (tag == N) {             *head.reference = a;             return *head.reference;         } else {             throw std::string{"Assignment type mismatch"};         }     }      inline Head_t& operator=(Head_t&& a) {         if (tag == N) {             *head.reference = std::move(a);             return *head.reference;         } else {             throw std::string{"Assignment type mismatch"};         }     } };  template <typename Head_t> struct Element_t<0, Head_t> {     std::size_t tag;     union {         struct {             Head_t* reference;         } head;     };      inline Element_t(Head_t& a) : tag{0} {          head.reference = &a;      }      template<typename Func_t, typename T>     inline void dispatch(T&& a) {         if (tag == 0) {             Func_t{}(*head.reference, std::forward<T>(a));         }     } };  template <std::size_t N, typename... _Types> class heterogeneous_list;  template <std::size_t N, typename Head_t, typename... Tail> class heterogeneous_list<N, Head_t, Tail...> : public heterogeneous_list<N - 1, Tail...> {     Head_t Element; public:     using type_set = Element_t<N, Head_t, Tail...>;      type_set operator[](std::size_t index) {         if (index == N) {             return type_set(Element);         } else if constexpr (N > 0) {             return heterogeneous_list<N - 1, Tail...>::operator[](index);         } else {             throw std::string{"out of bound access"};         }     } };  template <typename Head_t> class heterogeneous_list<0, Head_t> {     Head_t Element; public:     using type_set = Element_t<0, Head_t>;      type_set operator[](std::size_t index) {         if (index == 0) {             return type_set(Element);         } else {             throw std::string{"out of bound access"};         }     } };      The main point is that decentralized features always win because each focuses on one, right now, we are using metaprogramming for everything and it's a mess! (Look above!!) On Wed, 8 Apr 2026, 9:52 pm Simon Schröder via Std-Proposals, <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]> > wrote: > On Apr 8, 2026, at 9:47 AM, Muneem <itfllow123_at_[hidden] <mailto:itfllow123_at_[hidden]> > wrote: > > int^ x = {1,2.5,”C++”}.select(2); This would throw a huge error at compile time and is exactly why we need a new expression type T^. Not using T^ would give too much freedom that could backfire as shown in the example. In fact for T^, you cant even assign float^ to an int^. May bad, I wasn’t clear enough with my example. I do understand that with index=2 the compiler should use the compile time version and return the correct type. What my question actually is what would happen if the index is a runtime index that happens to be 2. How would the code be compiled to handle this case? What would happen to following source code lines that use ‘x’? Or would you just throw an exception? Concerning reflection: We are not there yet with C++26. But, the ultimate goal is to be able to generate arbitrary code. And we will be able to read in entire functions, analyze them and then replace the function with a new one generated with reflection. (With C++26 we can only analyze classes and generate new ones based on this.) In my opinion, everything you say the compiler should generate for your type, reflection will be able to generate in one of the two next C++ versions. You should really learn about reflection; maybe watch one of Herb Sutter’s talk from last year. You’d be surprised what reflection can actually do right now. And you don’t give compilers enough credit which things they can plainly see through and optimize. std::tuple does not have any bookkeeping data: It just stores the elements of a heterogeneous list. The types stored inside the tuple are part of the tuple’s type itself and thus part of the function signature. You cannot store less data than this. There is no type possible which is more efficient than std::tuple. Note that std::tuple is not at all comparable to std::vector<std::variant>. Your proposed type sounds a lot more like std::vector<std::variant>. Even if it is a new type, as long as it is more like a vector of variants it will necessarily be slower than a std::tuple because your new type would include some bookkeeping (and std::tuple does not). I can assure your that std::tuple is already the most performant type possible for your heterogeneous list (unless you want a runtime resizable heterogeneous list—which is not necessary for your Turing virtual machine). The only thing left is how to get elements out of the std::tuple at runtime and what type should be used here. -- Std-Proposals mailing list Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals -- Std-Proposals mailing list Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals -- Std-Proposals mailing list Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals -- Std-Proposals mailing list Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals -- Std-Proposals mailing list Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals -- Std-Proposals mailing list Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals -- Std-Proposals mailing list Std-Proposals_at_[hidden] <mailto:Std-Proposals_at_[hidden]> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals -- Std-Proposals mailing list Std-Proposals_at_[hidden] https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2026-04-10 02:59:53