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 14:08:18 +0400
Sent from my Galaxy

When you have a working compiler we can continue the conversation.It's like you are saying:"Do what Eric Niebler did. Disappear for a couple of years, then come back with an awesome change that make us unhelpful to NOT use it".That is not efficient,-I will waste so much time,-It will promote individualism over collectivism-And by the time it is done, the world is already moved on.What can be done by one person in one year, can be done by 3 in 3 months.Anyway,It will be on my ToDo list.

From: organicoman <organicoman_at_[hidden]>
Sent: Friday, August 2, 2024 1:18:33 AM
To: Tiago Freire <tmiguelf_at_hotmail.com>; marcinjaczewski86_at_[hidden] <marcinjaczewski86_at_[hidden]>; std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] Function overload set type information loss
 


That's the change I'm proposing







Sent from my Galaxy






-------- Original message --------
From: Tiago Freire <tmiguelf_at_hotmail.com>
Date: 8/2/24 3:08 AM (GMT+04:00)
To: organicoman <organicoman_at_[hidden]>, marcinjaczewski86_at_[hidden], std-proposals_at_[hidden]

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



Do you have a compiler build that does this?

From: organicoman <organicoman_at_[hidden]>
Sent: Friday, August 2, 2024 12:55:47 AM
To: Tiago Freire <tmiguelf_at_[hidden]>; marcinjaczewski86_at_[hidden] <marcinjaczewski86_at_[hidden]>; std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] Function overload set type information loss
 


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 pass
1- parsed foo,
2- has function orthogonal template parameters ?
  - yes: tag as dual effective/apparent
  - no: pass
3- 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: error
5- replace effdecltype with void(*)(void)
6- parse consumeType
7- execute 2 only
8- enter main
9- parse vector
10- 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: pass
11- instantiation :
    11-a- is foo tagged
       yes: foo<T> : record T if T not recorded
       no: pass
12- enter loop 1
    12-a- execute 11 only
13- enter loop 2
14- 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); // 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.
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 type


struct __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 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.


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-02 10:08:28