C++ Logo

std-proposals

Advanced search

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

From: Tiago Freire <tmiguelf_at_[hidden]>
Date: Tue, 30 Jul 2024 09:09:42 +0000
I'm not entirely sure what is even being proposed here.
The examples don't help.

For example, foo appears to be extracting a compile time size from a runtime variable, which is unimplementable.

I think you need to provide a better explanation of what exactly you are proposing. Maybe what text you want to change.

It seems to misunderstanding some core concepts
of the language, if I had to guess C++ is not your primary programming language.

But I don't know, I don't understand what concept you are trying to describe.


________________________________
From: Std-Proposals <std-proposals-bounces_at_[hidden]> on behalf of organicoman via Std-Proposals <std-proposals_at_[hidden]>
Sent: Tuesday, July 30, 2024 10:39:40 AM
To: organicoman via Std-Proposals <std-proposals_at_[hidden]>
Cc: organicoman <organicoman_at_[hidden]>
Subject: Re: [std-proposals] Function overload set type information loss

This technique of apparent and effective type, could be generalized to C style arrays and variable templates.
For example:

size_t foo(const char* arr)
{
 //inside the function arr type is just a pointer
// but it's real type has an array extent N.

    using eff_type = effective_decltype(arr);
    return extract_array_size<eff_type>::value;
}

template<typename T, typename V >
int gInteger = 1234;

int main()
{
    char arr[]="cool_idea";
    foo(arr); // prints 9

    auto i = gInteger<double,char>;
    // type of i is integer
    std::is_same_v<
                effective_decltype(i),
               (int)<double, char>)>; //<- this is a new notation to express a variable template
}

The whole idea is revolving around keeping the real type of each object, even if it decays to pointer ( ex. Functions and Arrays), or it loses the full type signature ( like variable templates, and function templates).
After all, a C++ source code, is just a series of inspections and manipulation of types. So the more we keep information about types the more it is useful.


Hello Gents,
Let:
O(f) be the Set of 'function overloads' of the function named "f".

Let:
"Ret" be the return type of function "f".

The elements of O(f) can have 2 forms:

1-> Ret f ( Vargs )

2-> Ret f <Ts...> ( Fargs )

Where:
1-> "Vargs" is a variadic list of arguments types, participating or not in template type arguments deduction if "f" is a template. e.g:
template<typename T, typename V>
void f(T, V, double, int)
Vargs = {T, V, double, int}

2-> "Fargs" is a fixed list of arguments types, not related to any template type parameter. e.g:
template <typename T, typename V>
void f(double, int)
Fargs= {double, int} // T,V are not in the list.

"Ts" is just a variadic list of template parameters types.

The type of any function of the 1st form is:
decltype (f) = Ret(*)(Vargs),
which keeps information about the function's type template parameters participating in the function argument's list.

But the type of the 2nd form is:
decltype (f) = Ret(*)(Fargs);
No mater what the template parameters are, the type of the 2nd form always decays to:
Ret(*)(Fargs)

And always lose any type information about the function's type template parameters

Yet, when we want to get the address of such function, we are obligated to use the template types in the function name. e.g:
auto select_f = &f<Ts...>;

Otherwise we get overload ambiguity,
This is a proof that the template arguments participates in the function type.

In my opinion, the compiler should keep the template arguments type information.

I know that changing the type of "select_f" in the example above will break a lot of code.
But i have a suggestion.
If the compiler can keep record of :
* an apparent function type ; (the usual one)
   decltype (&f<Ts...>) = Ret(*)(Fargs)
* and an effective function type which is:
   decltype (&f<Ts...>) = Ret(*)<Ts...>(Fargs)

 This would fix the problem without breaking any neck.

Why is this useful?
Take this example:

struct Erased{
std::any (*m_fun) (void);

template<auto Func>
constexpr Erased()
  : m_fun(Func)
{ }

auto operator ()()
{
    using f_type = effective_decltype (m_fun);
    // imagin we have a type traits that
    // extracts template types.
   using T = extract_1st_template_type<f_type>;

    return std::any_cast<T>( m_fun(void) );
}

};

template <typename Ret>
std::any foo()
{ return Ret{}; }

int main()
{
  std::vector<Erased> vec;
  vec.push_back(Erased<&foo<int>>{});
  vec.push_back(Erased<&foo<double>>{});
  vec.push_back(Erased<&foo<some_type>>{});
  for(const auto& elem: vec)
 DoSomethingBasedOnReturnType(elem());
}

Using this technique we can store the template type, then recall it back.
I guess it will make type erasure more effecient.

Any thoughts?

Regards
Nadir


Sent from my Galaxy



Received on 2024-07-30 09:09:46