C++ Logo

std-proposals

Advanced search

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

From: organicoman <organicoman_at_[hidden]>
Date: Fri, 02 Aug 2024 02:55:47 +0400
Tiago,I got your analogy perfectly, you are reiterating what you said about how much to allocate...etc.Allow me to explain:1- main motivation:template<int T>int F(int x) { return T * x + 1; }int main(){ vector<int(*)(int)> vec; vec.push_back(&F<1>); vec.push_back(&F<2>); vec.push_back(&F<3>); for(const auto& f : vec) { cout << "function parameterized on t = " << /*some call to get the non-type template value*/ << endl; cout << "ret val =" << f(10) << endl; }}Despite the information about the template value is known, yet i connot access it from out side the function, why???Maybe i want to create an abacus of that function against the value of T!!That's the whole purpose of my proposal.I hear now, someone among you shouting, just return a pair<int,int>{ T, (T * x + 1) }Hhhh...that's the easy case.What if the T was a type template parameter, or worse, templated type template parameter!!My journey starts from that question. So a question that raises: How useful knowing an extra-information about some type?My original motivation stopped at knowing that type and maybe do some type traits manipulation only.But i found as side effect something else.example:template<typename T>void foo() { cout <<typeid(T).name()<<endl;}auto useType(effdecltype(foo) f) { using T = __get_ortho_at__<0>(f); f(); return T(0); }template<typename T>void consumeType(T t){ cout << typeid(t).name(); }int main(){ vector<effdecltype(foo)> vec; vec.push_back(&foo<int>); vec.push_back(&foo<double>); vec.push_back(&foo<float>); for(int i=0; i<5; ++i) vec.push_back(&foo<char>); for(const auto& F: vec) consumeType(useType(F)); }If that operator was implemented, this is how the example should be compiled.Compiler pre pass1- parsed foo, 2- has function orthogonal template parameters ? - yes: tag as dual effective/apparent - no: pass3- parsed useType 4- useType has an effdecltype param 4-a- is the expression inside effdecltype one of the 3 categories? - yes: has it orthogonal template parameter? - yes: execute 2 only , then goto 4-b - no: goto 5 4-b- start from return statement and track back to find where effdecltype mechanism is used - switch: * in return statement : goto 4-c * inside function body : goto X * nowhere: goto 5 4-c- check function retun type: is auto operator? - yes: tag function for virtual overload dispatch mechanism.(read down) goto 6 - no: error5- replace effdecltype with void(*)(void)6- parse consumeType7- execute 2 only8- enter main9- parse vector10- uses effdecltype in its template parameter. - yes: 1- read the expression of effdecltype then ask the compiler to record all orthogonal template parameters used to instantiate that expression (foo in our example) 2- replace effdecltype void(*)(void).inside vector template argument. 3- goto 11 - no: pass11- instantiation : 11-a- is foo tagged yes: foo<T> : record T if T not recorded no: pass12- enter loop 1 12-a- execute 11 only13- enter loop 214- consumeType is a function template 14-a- template parameter replaced by useType function. What is that function return type? - switch * concrete type : replace it in , then instantiate consumeType accordingly. * auto operator: is useType a tagged function? yes: generate virtual overload dispatch mechanism (read down) replace consumeType with the generated code no: error, substitution failure, cannot call a function template.15- execute a second compiler pass that excutes the current C++ standard.Explanation.In this compiler pre-pass, we discovered that useType function is using the effdecltype operator and we proved that it's return type and statement depends on it, thanks to the auto operator as a return type place holder.Thus we tag this function. And we tell the compiler to record all orthogonal template parameters used to instantiate the expression inside the effdecltype operator.In our case that expression is foo.So every time foo is instantiated, that orthogonal template parameter is recorded.At the end, the compiler will replace the auto place holder of useType function with a union made of all the types the compiler recorded.This union is tagged as compiler generated, that means, it has special meaning. If useType return value is assigned to a variable we have two cases.auto var1 = useType (F); // perfect capture double var2 = useType(F); // castingThe 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.Here the compiler generate a virtual overload dispatch mechanism (it is a made up name, nowhere in the standard)It will create an unnamed function void __Xidfc_consumType_(auto Xunion){ switch(Xunion.__X_index) { case 0: consumeType<int>(Xunion.__Xi); break; case 1: consumeType<double>(Xunion.__Xd); break; case 2: consumeType<float>(Xunion.__Xf); break; case 3:           consumeType<char>(Xunion.__Xc);           break;    }}Where, Xunion could be of typestruct __Xidfc_foo_eff{ int __X_index; union { int __Xi; double __Xd ; float __Xf; char __Xc };}; In the second case ( casting) which is the easiest, that line will be replaced bydouble var2 = *reinterpret_cast<double*> ((reinterpret_cast<char*>(&(useType()))+sizeof(int)); // int since __X_index is intNo 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.This is one approach. It relys on static data analysis, and some compiler planner to prove that what it generates as code is correct, before passing to the next compilation phase which is the usual process.I hope i made the jagged hills somehow even.Nadir

Received on 2024-08-01 22:56:00