C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Function overload set type information loss

From: Breno Guimarães <brenorg_at_[hidden]>
Date: Sat, 3 Aug 2024 14:49:01 -0300
And what would we get for a template taking arbitrary type?

Em sáb., 3 de ago. de 2024 14:43, organicoman <organicoman_at_[hidden]>
escreveu:

> Not clear at all. You changed my question into another question and
> answered that instead.
>
> I asked for which type we should instantiate the template that is being
> invoked with argument "t" for which you don't know the type.
>
> Are you talking about your example? this one:
>
> for (auto elem: vec)
> {
> auto r = elem();
> auto t = r.get(); //<-is this the t you are asking?
> useType(t);
> }
>
> If it is the case, then it means that you misunderstood the signature of
> useType....please read the post carefully.
>
> useType takes a function pointer, not variable of arbitrary type
>
> Look what i wrote: (copy/paste)
> auto useType(effdecltype(foo) f)
> {
> using T = __get_ortho_at__<0>(f);
> f();
> return T(0);
>
> }
>
> If I am mistaken then, copy/paste the code from my example and point
> where you want more explanation.
>
>
>
>
> Not clear at all. You changed my question into another question and
> answered that instead.
>
> I asked for which type we should instantiate the template that is being
> invoked with argument "t" for which you don't know the type.
>
> Em sáb., 3 de ago. de 2024 12:47, organicoman <organicoman_at_[hidden]>
> escreveu:
>
>> Oooh i get you now.
>> Let me simplify that for you by an example.
>>
>> *auto* someFunction();
>>
>> Can you tell what this function's return type?
>>
>> No.
>>
>> So let me give you help about the implementation of this function.
>>
>> 1- this function returns an unnamed type.
>> 2- this unnamed type is an aggregate
>> 3- at offset 8 bytes there is a union of unknown members count and types.
>>
>> Asked:
>> Read the value of the union member.
>>
>> User asks:
>> But to what type should i cast that union to?
>>
>> Answer:
>> That's your responsibility.....Unless....
>> You use my tool.
>>
>> Users asks:
>> What is it?
>>
>> Answer:
>> You create a template function, you tune its body to execute whatever you
>> want based of whatever type you want, and my tool will take in charge the
>> responsibility of which overload it should call.
>>
>> That's in basic language what is happening.
>>
>> You don't need to know what type in the union.
>>
>> If you want to adventure and cast it to a specific type, you have two
>> possibility:
>> * can get what you want,
>> * you can get type mismatch => garbage.
>>
>> All that, is orchestrated by the pre-pass, so it is preferable to let the
>> tool handle the case.
>>
>> What about now? Clear?
>>
>>
>>
>>
>>
>>
>>
>> Sent from my Galaxy
>>
>>
>> -------- Original message --------
>> From: Breno Guimarães <brenorg_at_[hidden]>
>> Date: 8/3/24 6:56 PM (GMT+04:00)
>> To: organicoman <organicoman_at_[hidden]>
>> Cc: Std-Proposals <std-proposals_at_[hidden]>, Tiago Freire <
>> tmiguelf_at_[hidden]>
>> Subject: Re: [std-proposals] Function overload set type information loss
>>
>> You didn't see how that doesn't work yet?
>> You don't know the possible types inside xunion because of the multiple
>> TU thing. So you don't know how to fully implement operator==(xunion,int).
>> Other translation units can't help you implement that either because they
>> don't even know an operator== is needed since that is in a body of another
>> cpp file they never saw.
>>
>> But let's see a perhaps more obvious example:
>>
>> for (auto elem: vec)
>> {
>> auto r = elem();
>> auto t = r.get();
>> useType(t);
>> }
>>
>> "r" is the fancy xunion. The you call get on it. So you need to do the
>> virtual dispatch thing. But you don't know the type of "t", because
>> different types will return different things when you call "get()" on them.
>> So how would you know for which types to instantiate useType?
>>
>> Em sáb., 3 de ago. de 2024 11:22, organicoman <organicoman_at_[hidden]>
>> escreveu:
>>
>>> I don't think I was clear enough.
>>>
>>> What if you have:
>>> for (auto elem : vec)
>>> {
>>> auto r = useType(elem);
>>> if (r == 1)
>>> consumeType(r);
>>> }
>>>
>>> The compiler on this translation unit doesn't know all possible types
>>> (because the vec is global and filled in other TUs like you antecipated).
>>>
>>> So this translation unit could reason about the types it knows and
>>> generate the code for "if (r == 1)" but what about the types it doesn't
>>> know?
>>> The other translation units don't know the body of the for-loop so they
>>> wouldn't be able to fill in that gap either.
>>>
>>> In this translation unit we could generate the if for "int" but another
>>> one could require for float. Another translation unit could even push a
>>> std::string which would make this code a syntax error.
>>>
>>> That's why I asked if it was a limitation that you couldn't do anything
>>> other than directly call consumeType.
>>>
>>> The temporary value generated by useType would also have to be destroyed
>>> and that's a variation of the if problem because it typically is destroyed
>>> on the caller body. So you wouldn't know which destructor to call.
>>>
>>> I didn't understand your question very well, but i will use your example
>>> and try to detail it.
>>>
>>> So let analyze this:
>>>
>>> if(r == 1)
>>>
>>> For the compiler, it sees it as
>>>
>>> if(operator == (r, 1))// bool operator==(xunion,int)
>>>
>>> It directly replace it by the cast i described previously.
>>> And it will become
>>>
>>> if(
>>> *reinterpret_cast<int*>
>>> ((reinterpret_cast<char*>(&(r))+sizeof(void*))
>>> == 1)
>>>
>>> And it becomes a regular comparison, calling
>>>
>>> bool operator(int, int)
>>>
>>> Remember what i said in that post:(copy/past)
>>>
>>> ------start------
>>> "...
>>> In the second case ( casting) which is the easiest, that line will be
>>> replaced by
>>>
>>> double var2 = *reinterpret_cast<double*>
>>> ((reinterpret_cast<char*>(&(useType()))+sizeof(int)); // int since
>>> __X_index is int
>>>
>>> No mater what is the union's active member.
>>> The user have to take responsibility of his code.
>>> And it is safe to still the guts of a temporary.
>>> ..."
>>> ------end--------
>>>
>>> The user takes responsibility to what he wants to cast to.
>>>
>>> You should package all operation inside a template function like
>>> consumeType.
>>>
>>> You can think of consumeType as std::visit
>>> which was written for you by your servant Mr.compiler.
>>>
>>> Did I answer your question?
>>>
>>> -------- Original message --------
>>> From: Breno Guimarães <brenorg_at_[hidden]>
>>> Date: 8/3/24 5:43 PM (GMT+04:00)
>>> To: organicoman <organicoman_at_[hidden]>
>>> Cc: Std-Proposals <std-proposals_at_[hidden]>, Tiago Freire <
>>> tmiguelf_at_[hidden]>
>>> Subject: Re: [std-proposals] Function overload set type information loss
>>>
>>> I don't think I was clear enough.
>>>
>>> What if you have:
>>> for (auto elem : vec)
>>> {
>>> auto r = useType(elem);
>>> if (r == 1)
>>> consumeType(r);
>>> }
>>>
>>> The compiler on this translation unit doesn't know all possible types
>>> (because the vec is global and filled in other TUs like you antecipated).
>>>
>>> So this translation unit could reason about the types it knows and
>>> generate the code for "if (r == 1)" but what about the types it doesn't
>>> know?
>>> The other translation units don't know the body of the for-loop so they
>>> wouldn't be able to fill in that gap either.
>>>
>>> In this translation unit we could generate the if for "int" but another
>>> one could require for float. Another translation unit could even push a
>>> std::string which would make this code a syntax error.
>>>
>>> That's why I asked if it was a limitation that you couldn't do anything
>>> other than directly call consumeType.
>>>
>>> The temporary value generated by useType would also have to be destroyed
>>> and that's a variation of the if problem because it typically is destroyed
>>> on the caller body. So you wouldn't know which destructor to call.
>>>
>>>
>>>
>>>
>>> Em sáb., 3 de ago. de 2024 10:08, organicoman <organicoman_at_[hidden]>
>>> escreveu:
>>>
>>>> That means you can only use these pointers with consumeType?
>>>>
>>>> If you do a for loop on the vec you can only call
>>>> consumeType(useType(F))?
>>>> Because if you do other things inside the for loop in one translation
>>>> unit, the compiler won't have visibility of that in the second translation
>>>> unit. So it would not know to generate the code for the for loop body in
>>>> the first translation unit for the types in the second translation unit.
>>>>
>>>> Would that be a limitation? Or is there another way around that?
>>>>
>>>> As far as the vector is concerned, its elements type is void(*)(),
>>>> check the algorithm step 10-2
>>>>
>>>> And if you recall in the explanation of that algorithm, i said:
>>>> (copy/paste)
>>>>
>>>> -------- start---------
>>>>
>>>> "....
>>>>
>>>> If useType return value is assigned to a variable we have two cases.
>>>>
>>>> auto var1 = useType (F); // perfect capture
>>>> double var2 = useType(F); // casting
>>>>
>>>> The first case, which is the most interesting and powerful, is most
>>>> useful if you pass that var1 to a function template, consumeType, in our
>>>> example above
>>>> ...."
>>>> -------end-----
>>>>
>>>> So, all revolves around useType, and its usage scope.
>>>>
>>>> Also, since the vector is a local variable to that translation unit,
>>>> then the scope of useType is just the instantiated foo<T> in that
>>>> unit. The rest is irrelevant.
>>>> You don't pay for what you don't need.
>>>>
>>>> Allow me to anticipate one question.
>>>>
>>>> What if the vector was a global variable, filled accross translation
>>>> unit?
>>>> *It doesn't matter*.
>>>> The linker is doing the concatenation algorithm that i described to
>>>> you, so all is fine and dandy.
>>>>
>>>> Let's use an example:
>>>> -----Source1.cpp
>>>> #include "*temp.h*"
>>>>
>>>> extern vector<effdecltype(foo)> globalVector;
>>>>
>>>> /// no local insertion into the global vector
>>>> /// use it directly in the loop
>>>> for(F : globalVector)
>>>> consumeType(useType(F));
>>>>
>>>> -----Source2.cpp
>>>> #include "*temp.h*"
>>>>
>>>> extern vector<effdecltype(foo)> globalVector;
>>>>
>>>> /// just fill the global vector...
>>>> globalVec.push_back(foo<int>);
>>>> globalVec.push_back(foo<double>);
>>>>
>>>> *Explanation*:
>>>> In the 1st translation unit, the virtual overload dispatch mechanism (read
>>>> explanation in the post about the pre-pass algorithm)
>>>> sees that, in this translation unit, the compiler didn't record any
>>>> instance of foo<T>, so it emits an error (substitution failure cannot
>>>> call a template function consumeType), *that if the vector was a local
>>>> variable*, but because the compiler knows it is a global variable (remember
>>>> that the vector is the entity which triggered this mechanism), then
>>>> it replaces the whole overload dispatch switch with just the name of the
>>>> generated function and let the linker resolve it. Like the following.
>>>>
>>>> ----starts like this
>>>>
>>>> for(F: globalVector)
>>>> consumeType(useType(F));
>>>>
>>>> ....becomes
>>>>
>>>> // generared pre-pass code
>>>> extern struct __X_foo_eff;
>>>> __X_foo_eff useType(effdecltype(foo));
>>>> void __X_consumeType(__X_foo_eff);
>>>>
>>>> // down in code
>>>> for(F: globalVector)
>>>> __X_consumeType(useType(F));
>>>>
>>>> The rest is known to everyone.
>>>>
>>>> The overall observation about this mechanism, is that, it doesn't leak
>>>> type information to the runtime.
>>>> I don't have a table let say
>>>> hash(type)<->function_ptr
>>>> Or
>>>> textMangle(type)<->function_ptr
>>>> Usually used by std::any or, some reflection algorithms.
>>>> Which make this approach safe against reverse engineering the {hash,
>>>> text}, and doesn't break the ASLR safety mechanism by saving function
>>>> pointer in permanent global container.
>>>>
>>>> I hope it is clear..... at this point, if everyone understood the
>>>> fundamental of this paper then I guess that you can start contributing.
>>>>
>>>> That's all.
>>>>
>>>> Em sex., 2 de ago. de 2024 23:25, organicoman <organicoman_at_[hidden]>
>>>> escreveu:
>>>>
>>>>>
>>>>> So how would you generate the switch for each type given that some
>>>>> instantiations of the template can happen only in another translation unit?
>>>>>
>>>>> Let me illustrate with an example:
>>>>>
>>>>> Let's put in header file
>>>>>
>>>>> --- *temp.h*
>>>>>
>>>>> foo<T>
>>>>>
>>>>> consumeType <T>
>>>>>
>>>>> useType(effdecltype(foo) f)
>>>>>
>>>>> --- source1.cpp
>>>>>
>>>>> #include "*temp.h*"
>>>>>
>>>>> ///// adding instances
>>>>>
>>>>> vec.push_back(foo<int>);
>>>>>
>>>>> vec.push_back (foo<double>);
>>>>>
>>>>> /// calling function inside loop
>>>>>
>>>>> consumeType(useType(F)); //<- to be replaced
>>>>>
>>>>> --- source2.cpp
>>>>>
>>>>> #include "*temp.h*"
>>>>>
>>>>> ///// adding instances
>>>>>
>>>>> vec.push_back(foo<float>);
>>>>>
>>>>> vec.push_back (foo<char>);
>>>>>
>>>>> /// calling function inside loop
>>>>>
>>>>> consumeType(useType(F)); //<- to be replaced
>>>>> *Compilation pre-pass:*
>>>>> Source1: pseudo code only (don't hate me)
>>>>> useType:
>>>>> case foo<int>:
>>>>> return xunion{int}
>>>>> case foo<double>:
>>>>> return xunion{double};
>>>>> Source2:
>>>>> useType:
>>>>> case foo<float>:
>>>>> return xunion{float}
>>>>> case foo<char>:
>>>>> return xunion{char}
>>>>>
>>>>> *Compilation pass:*
>>>>> Since useType is tagged then add a bogus jump instruction that will
>>>>> never be taken in that translation unit.
>>>>> Source1.o: pseudo assembly
>>>>> cmp esi, foo<int>;
>>>>> je LBL_xunion_int ;
>>>>> cmp esi,foo<double>
>>>>> je LBL_xunion_double
>>>>> jmp 0xfffffff; // hook jump
>>>>> source2.o:
>>>>> cmp esi, foo<float>
>>>>> je LBL_xunion_float
>>>>> cmp esi, foo<char>
>>>>> je LBL_xunion<char>
>>>>> jmp 0xfffffff // hook jump
>>>>> *Linker: source1.o source2.o main.o*
>>>>> 1- read mangled name of function
>>>>> 2- is it tagged as effdecltype
>>>>> no: do your usual job, then goto 6
>>>>> 3- is there any previous cmp address saved?
>>>>> yes: replace the 0xfffff of the hook jmp with the address of the
>>>>> saved cmp.
>>>>> 4- save the current first cmp address.
>>>>> 5- resolve mangled name to be the current function address.
>>>>> 6- move to next
>>>>> 7- repeat if not finished.
>>>>>
>>>>> *Explanation:*
>>>>> Let's assume the worst case scenario, where the two translation unit
>>>>> are compiled separately.
>>>>> Because if they were together, the compiler detects and fuse the two
>>>>> generated function assembly into one, by concatenating the switch cases.
>>>>>
>>>>> Ok, I guess now, everyone knows what happens in the compiler pre-pass.
>>>>> So at the end of it, we will have, for each translation unit an object
>>>>> file with different switch for the same function name.
>>>>> Let's tackle the assembly now.
>>>>> Because the compiler already knows, that useType is a tagged
>>>>> function, it will add to its assembly a bogus jump instruction, that is
>>>>> proven it will never be taken for that translation unit only.
>>>>> jmp 0xFFFFFFFF;
>>>>> Then if we want to use both *.o object files. Things are taken
>>>>> differently.
>>>>> Here comes the linker job.
>>>>> On the command line, the linker parses the *.o from left to right,
>>>>> like every one knows.
>>>>> As soon as it detects the mangled name of useType, it knows that it
>>>>> is tagged, it saves the address of the first compare instruction cmp
>>>>> esi, foo<T> then carry on its job, when it enters the second object
>>>>> file, it finds the same useType mangled name, thus it knows it is
>>>>> tagged, it checks if it has any compare instruction saved from previous *.o
>>>>> object file then stamps its address into the bogus jump instruction of the
>>>>> current object file then saves the first cmp of the current object
>>>>> file instruction, then marks the mangled name as resolved with address of
>>>>> useType in this object file.... and so on....
>>>>>
>>>>> current *.o
>>>>> jmp 0xfffffff <-> becomes <-> jmp lea prev[cmp esi, foo<int>] //
>>>>> illustration only don't hate me
>>>>>
>>>>> until the last object file that contains the same mangled name, at
>>>>> that point, it created a linked list between compare instructions.
>>>>> The mangled useType will be resolved to the last same mangled name
>>>>> seen, thus it becomes the entry point of the useType function.
>>>>>
>>>>> It's like you are concatenating switch statement.
>>>>> One observation worth reiterating,
>>>>> Like i said in last reply, we will have code bloat.
>>>>> That's because the body of useType before the switch statement will
>>>>> be deduplicated as many as it is generated, and all, except one, will be
>>>>> execute, the rest is dead meat never executed.
>>>>> From this, i recommand to make the body of functions like useType
>>>>> very small, let consumeType do the heavy lifting.
>>>>> Or make the linker smart to fuse the whole thing into one giant switch.
>>>>> But the first recommendation is faster to implement.
>>>>>
>>>>> That's one simple approach, many other are way efficient. But we need
>>>>> a compiler planner to detect best decisions. They will work in tendem.
>>>>>
>>>>> That's all.
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Em sex., 2 de ago. de 2024 19:48, organicoman <organicoman_at_[hidden]>
>>>>> escreveu:
>>>>>
>>>>>>
>>>>>>
>>>>>> So it doesn't work for multiple translation units?
>>>>>>
>>>>>> It perfectly does.
>>>>>>
>>>>>> Because for each translation unit, you tag the functions and generate
>>>>>> the code, stamp it, then pass it the compilation step...and so on,
>>>>>>
>>>>>> There is no deduplication of function names, nor generation of
>>>>>> extraneous code. You have exactly what you need in that unit.
>>>>>>
>>>>>> Just one thing.
>>>>>>
>>>>>> Sometimes the generated code could have the same instructions but
>>>>>> under different function name, that causes bloating, thus a need to a
>>>>>> compiler planner.
>>>>>>
>>>>>> Look the idea is veeeeeery basic.
>>>>>>
>>>>>> I consider a C++ source code, after the compiler does template
>>>>>> instantiation, as a type's database. Unfortunately the current standards
>>>>>> does not allow us to query anything from it.
>>>>>>
>>>>>> Imagine if you have the power to query how many types your program
>>>>>> uses, what is the type-size distribution chart, sorting by type size, by
>>>>>> layout size.... etc
>>>>>>
>>>>>> This is a data mine to optimize your code, reason about its
>>>>>> implementation and make it more fast and efficient.
>>>>>>
>>>>>> That's what i am trying to start.
>>>>>>
>>>>>> I don't see the extent of my proposal but for sure, with some help,
>>>>>> it will be developed to full potential.
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> Em sex., 2 de ago. de 2024 19:16, organicoman via Std-Proposals <
>>>>>> std-proposals_at_[hidden]> escreveu:
>>>>>>
>>>>>>> Your solution requires at compile time information that is only
>>>>>>> available at runtime.
>>>>>>>
>>>>>>> I don't understand what runtime data you are referring to.
>>>>>>>
>>>>>>> In the algorithm explained before. All the data, that the compiler's
>>>>>>> pre-pass is manipulating, are compile time data.
>>>>>>> At the level of a translation unit. A function with *orthogonal
>>>>>>> template parameters* (foo<T>)
>>>>>>> cannot exist unless you instantiate it.
>>>>>>> Add to that, the function is tagged as per the algorithm, so it is
>>>>>>> prone to record the types it was instantiated for.
>>>>>>> But as an optimization, the compiler will not record the list of *orthogonal
>>>>>>> **template parameters* unless it sees that some variable is using
>>>>>>> it, otherwise no need to generate any code.
>>>>>>> The compiler confirmed that by the usage of the effdecltype
>>>>>>> operator inside the template argument of the vector in the example.
>>>>>>> Every time you instantiate foo with a type, you have to write it
>>>>>>> explicitly in the source code.
>>>>>>> foo<int>, foo<double>...etc
>>>>>>> Next to that, the address of a function is a constexpr value, you
>>>>>>> can use it as a non-type template parameter.
>>>>>>> So as a summary, the list of *orthogonal template parameters* is
>>>>>>> fetchable from the source code, and the address of the instances is
>>>>>>> constexpr value.
>>>>>>> These are the ingredients to make the algorithm work.
>>>>>>>
>>>>>>> Even if the compiler could trace data across any type of data
>>>>>>> container (which it won't),
>>>>>>>
>>>>>>> It doesn't need to, because the information needed is the *orthogonal
>>>>>>> template parameters* not the number of elements in the container.
>>>>>>> You can have 100 element of type foo<int>, but the list of *orthogonal
>>>>>>> template parameters* is only { int }
>>>>>>> It's like you are counting how many instances of foo<T> you have in
>>>>>>> that translation unit.
>>>>>>>
>>>>>>> datapoints in container could take the form of any possible
>>>>>>> combination on any position and be influenced to translation units not
>>>>>>> visible at compile time. But for your solution to work it requires a fix
>>>>>>> layout (which in practice doesn't happen) and perfect observability.
>>>>>>> This is not a problem that needs to be solved, it's just how things
>>>>>>> work.
>>>>>>>
>>>>>>> Let me reiterate.
>>>>>>> Look at this snippet:
>>>>>>>
>>>>>>> vec.push_back(&foo<int>);
>>>>>>> int random;
>>>>>>> cin >> random;
>>>>>>> while(random - -) vec.push_back(&foo<char>);
>>>>>>> cin >> random;
>>>>>>> random = min(random, vec.size());
>>>>>>> while(random - -) vec.pop_back();
>>>>>>>
>>>>>>> As you can see, i cannot tell nor track the vector size. But it is
>>>>>>> irrelevant, because i have only two function pointer => { int, char }
>>>>>>> And by consequence my switch statment will contain only two cases:
>>>>>>>
>>>>>>> case &foo<int>:
>>>>>>> return __Xic_foo_eff_{ &foo<int>, int(0) };
>>>>>>> case &foo<char>:
>>>>>>> return __Xic_foo_eff_{ &foo<char>, char(0) };
>>>>>>>
>>>>>>> The two case lables are constexpr values known at compile time.
>>>>>>>
>>>>>>> You cannot resolve this problem without appending extra data to
>>>>>>> containers that is used to inform about the nature of the data. And you
>>>>>>> cannot solve this without a runtime check and control flow to browse for
>>>>>>> the right handler.
>>>>>>> This is a problem of distinguishability and computability, it is not
>>>>>>> something you can solve, it's a consequence of math as described by Shannon.
>>>>>>>
>>>>>>> That's why std:any has additional data related to runtime type
>>>>>>> information, and that's why it always needs some form of selection or hash
>>>>>>> table or equivalent.
>>>>>>>
>>>>>>> Well, i just proved to you that i don't need that data, and that
>>>>>>> this approach is plausibly more effecient and safe.
>>>>>>> Let's try to stretch it more, shall we?
>>>>>>>
>>>>>>> The way it's already done is already how this problem should be
>>>>>>> solved, no new feature necessary. What you are proposing requires the
>>>>>>> suspension of the laws of physics.
>>>>>>>
>>>>>>>
>>>>>>> ------------------------------
>>>>>>> *From:* organicoman <organicoman_at_[hidden]>
>>>>>>> *Sent:* Friday, August 2, 2024 9:44:09 PM
>>>>>>> *To:* Tiago Freire <tmiguelf_at_[hidden]>;
>>>>>>> std-proposals_at_[hidden] <std-proposals_at_[hidden]>
>>>>>>> *Subject:* Re: [std-proposals] Function overload set type
>>>>>>> information loss
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> And also unimplementable...
>>>>>>> But you don't have to take my word for it, you can try it yourself.
>>>>>>>
>>>>>>> But ... that's the whole purpose of this thread.
>>>>>>> Putting down an idea in form of a paper, or an algorithm and tackle
>>>>>>> any contradiction it raise untill we hit the wall or we make it through.
>>>>>>> And we have just started agreeing about the core idea. Which is
>>>>>>> Adding an operator that does overload dispatch and code
>>>>>>> generation...etc
>>>>>>>
>>>>>>> If it is not implementable, then point where, i will work on it...
>>>>>>>
>>>>>>> ------------------------------
>>>>>>> *From:* Std-Proposals <std-proposals-bounces_at_[hidden]> on
>>>>>>> behalf of organicoman via Std-Proposals <
>>>>>>> std-proposals_at_[hidden]>
>>>>>>> *Sent:* Friday, August 2, 2024 9:15:36 PM
>>>>>>> *To:* organicoman via Std-Proposals <std-proposals_at_[hidden]>
>>>>>>> *Cc:* organicoman <organicoman_at_[hidden]>
>>>>>>> *Subject:* Re: [std-proposals] Function overload set type
>>>>>>> information loss
>>>>>>>
>>>>>>> Hello Gašper,
>>>>>>> Since participants are starting to get what I am talking about, then
>>>>>>> allow me to answer your pending question.
>>>>>>> This is what you've said in previous post.
>>>>>>>
>>>>>>> There is another fundamental misunderstanding here.
>>>>>>>
>>>>>>> The feature calls to "record all the orthogonal template parameters".
>>>>>>>
>>>>>>> While that's not what that word means, I think regardless it's
>>>>>>> asking for global enumeration. C++ has a separate linking model. All the
>>>>>>> types/functions are not known within a single unit. The xunion is
>>>>>>> impossible to compute.
>>>>>>>
>>>>>>> This is a sister problem to what Jason is highlighting.
>>>>>>>
>>>>>>> When i was describing my algorithm, i left a big X mark, hoping
>>>>>>> that someone will ask me, if people were attentive.
>>>>>>> Line:
>>>>>>> 4-b-
>>>>>>> switch
>>>>>>> *.....
>>>>>>> * inside function body: goto X
>>>>>>>
>>>>>>> Then I left a hint at the bottom of the post in a form of code
>>>>>>> comment
>>>>>>>
>>>>>>> double var2 = *reinterpret_cast<double*>
>>>>>>> ((reinterpret_cast<char*>(&(useType()))+sizeof(int)); // int since
>>>>>>> __X_index is int.
>>>>>>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>>>>>>> ^^^^^
>>>>>>>
>>>>>>> The __X_index is not of type int but of type void*, the unnamed
>>>>>>> struct that the compiler generate becomes:
>>>>>>> struct __Xidfc_foo_eff
>>>>>>> {
>>>>>>> void* __X_index;
>>>>>>> union
>>>>>>> { int __Xi; double __Xd ; float __Xf; char __Xc };
>>>>>>> };
>>>>>>>
>>>>>>> Since the compiler tagged useType function, as described by the
>>>>>>> algorithm, then after finishing recording the orthogonal template
>>>>>>> parameters, it replaces the body with the following.
>>>>>>>
>>>>>>> auto useType(effdecltype(foo) f)
>>>>>>> {
>>>>>>> f();
>>>>>>> switch(f)
>>>>>>> {
>>>>>>> case &f<int>: //<- the pointer value
>>>>>>> return __Xidfc_foo_eff{(void*) &f<int>, int(0)};
>>>>>>>
>>>>>>> case &f<double>:
>>>>>>> return __Xidfc_foo_eff{(void*) &f<double>, double(0)};
>>>>>>>
>>>>>>> case &f<float>:
>>>>>>> return __Xidfc_foo_eff{(void*) &f<float>, float(0)};
>>>>>>>
>>>>>>> case &f<char>:
>>>>>>> return __Xidfc_foo_eff{(void*) &f<char>, char(0)};
>>>>>>> }
>>>>>>> }
>>>>>>>
>>>>>>> Pointers are comparable for equality.
>>>>>>>
>>>>>>> As you can see, you can capture the xunion.... right? There is no
>>>>>>> fundamental misunderstanding ...
>>>>>>> Plus everything is done by the compiler, there is no global map, no
>>>>>>> find algorithm, no type hashs, no type erasure (heap allocation), no manual
>>>>>>> intervention. All is automated at compile-time.
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Std-Proposals mailing list
>>>>>>> Std-Proposals_at_[hidden]
>>>>>>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>>>>>>
>>>>>>>

Received on 2024-08-03 17:49:18