Home » Community » U++ community news and announcements » get_i
get_i [message #54247] |
Sun, 14 June 2020 19:10  |
 |
mirek
Messages: 14255 Registered: November 2005
|
Ultimate Member |
|
|
A complemement to findarg and decode:
get_i(-1, "zero", "one", "two") = zero
get_i(0, "zero", "one", "two") = zero
get_i(2, "zero", "one", "two") = two
get_i(3, "zero", "one", "two") = two
|
|
|
Re: get_i [message #54250 is a reply to message #54247] |
Tue, 16 June 2020 17:45   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
Thank you.
I played a little bit with get_i and godbolt.org and got results below.
Test: const char* c = get_i(-1, "zero", "one", "two");
Assembly for the original code (-O2):
.LC0:
.string "zero"
_GLOBAL__sub_I_c:
mov QWORD PTR c[rip], OFFSET FLAT:.LC0
ret
c:
.zero 8
I changes U++ code a little bit:
template <class T> constexpr const T& min(const T& a, const T& b) { return a < b ? a : b; }
template <class T> constexpr const T& max(const T& a, const T& b) { return a > b ? a : b; }
template <class T> // deprecated name, use clamp
constexpr T minmax(T x, T _min, T _max) { return min(max(x, _min), _max); }
template <class T>
constexpr T clamp(T x, T _min, T _max) { return minmax(x, _min, _max); }
inline constexpr const char *get_i(int i, const char *p0, __List##I(E__NFValue)) \
Resulting assenbly:
.LC0:
.string "zero"
c:
.quad .LC0
Conclusion: "constexpr" is quite useful ...
Regards,
Novo
|
|
|
|
|
Re: get_i [message #54254 is a reply to message #54253] |
Tue, 16 June 2020 18:21   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
Another experiment/suggestion.
I rewrote get_i using variadic template:
template <typename A, typename... T>
constexpr A get_i(int i, const A& p0, const T& ...args)
{
A list[] = {p0, args...};
int n = sizeof...(args);
return list[clamp(i, 0, n)];
}
const char* cr = get_i(1, "zero", "one", "two");
RDUMP(cr);
int ir = get_i(1, 0, 1, 2);
RDUMP(ir);
IMHO, my implementation is much shorter and it will compile faster.
IMHO, macroses __List and __Expand are not needed anymore ...
Regards,
Novo
|
|
|
Re: get_i [message #54256 is a reply to message #54254] |
Tue, 16 June 2020 21:20   |
 |
mirek
Messages: 14255 Registered: November 2005
|
Ultimate Member |
|
|
Novo wrote on Tue, 16 June 2020 18:21Another experiment/suggestion.
I rewrote get_i using variadic template:
template <typename A, typename... T>
constexpr A get_i(int i, const A& p0, const T& ...args)
{
A list[] = {p0, args...};
int n = sizeof...(args);
return list[clamp(i, 0, n)];
}
const char* cr = get_i(1, "zero", "one", "two");
RDUMP(cr);
int ir = get_i(1, 0, 1, 2);
RDUMP(ir);
IMHO, my implementation is much shorter and it will compile faster.
IMHO, macroses __List and __Expand are not needed anymore ...
Yes, you are right about this, I have used old tricks mostly out of habit. I guess I will have to rewrite it all now 
However, constexpr I still do not agree. Following your logic, we should add constexpr to every single function everywhere - these are as likely to have constant parameters as get_i (which has like 0.00000001% chance that first parameter will be const in real code).
|
|
|
Re: get_i [message #54257 is a reply to message #54254] |
Tue, 16 June 2020 21:22   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
Another implementation using initializer_list:
template <typename T>
constexpr T get_i(int i, std::initializer_list<T> list)
{
return list[clamp(i, 0, list.size())];
}
This one is trictly-typed, although I couldn't check assembly with godbolt because it complains about something ...
Regards,
Novo
|
|
|
|
Re: get_i [message #54259 is a reply to message #54258] |
Tue, 16 June 2020 21:42   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
mirek wrote on Tue, 16 June 2020 15:25Novo wrote on Tue, 16 June 2020 21:22Another implementation using initializer_list:
template <typename T>
constexpr T get_i(int i, std::initializer_list<T> list)
{
return list[clamp(i, 0, list.size())];
}
This one is trictly-typed, although I couldn't check assembly with godbolt because it complains about something ...
Nah, we do not want strict typing here.
Sorry, last one won't compile.
The one using variadic template is fine, although it still needs specialization for const char* ... :-/
Regards,
Novo
|
|
|
Re: get_i [message #54260 is a reply to message #54256] |
Tue, 16 June 2020 22:15   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
mirek wrote on Tue, 16 June 2020 15:20
However, constexpr I still do not agree. Following your logic, we should add constexpr to every single function everywhere - these are as likely to have constant parameters as get_i (which has like 0.00000001% chance that first parameter will be const in real code).
You cannot add constexpr to every single function everywhere. There are restrictions ...
But, IMHO, function, which can be compiled with constexpr, should have it ...
At this time you are not using functions in compile-time context because you just cannot do that.
I personally often write code like this:
enum e {
e01 = 1,
e02 = 100,
e03 = e01 + e02
};
In case of constexpr functions I'll be able to write this:
enum e {
e01 = min(something, something_else),
e02 = max(something, something_else)
};
Another observation: template functions/methods are inline by default.
Regards,
Novo
|
|
|
Re: get_i [message #54261 is a reply to message #54256] |
Wed, 17 June 2020 00:01   |
 |
mirek
Messages: 14255 Registered: November 2005
|
Ultimate Member |
|
|
mirek wrote on Tue, 16 June 2020 21:20Novo wrote on Tue, 16 June 2020 18:21Another experiment/suggestion.
I rewrote get_i using variadic template:
template <typename A, typename... T>
constexpr A get_i(int i, const A& p0, const T& ...args)
{
A list[] = {p0, args...};
int n = sizeof...(args);
return list[clamp(i, 0, n)];
}
const char* cr = get_i(1, "zero", "one", "two");
RDUMP(cr);
int ir = get_i(1, 0, 1, 2);
RDUMP(ir);
IMHO, my implementation is much shorter and it will compile faster.
IMHO, macroses __List and __Expand are not needed anymore ...
Yes, you are right about this, I have used old tricks mostly out of habit. I guess I will have to rewrite it all now 
Do you see any problems?
template <class T, class V>
constexpr V decode(const T& sel, const V& def)
{
return def;
}
template <class T>
constexpr const char *decode(const T& sel, const char *def)
{
return def;
}
template <class T, class K, class V, typename... L>
constexpr V decode(const T& sel, const K& k, const V& v, const L& ...args)
{
return sel == k ? v : (V)decode(sel, args...);
}
template <class T, class K, typename... L>
constexpr const char *decode(const T& sel, const K& k, const char *v, const L& ...args)
{
return sel == k ? v : decode(sel, args...);
}
template <class T, class K>
constexpr int findarg(const T& x, const K& k)
{
return x == k ? 0 : -1;
}
template <class T, class K, typename... L>
constexpr int findarg(const T& sel, const K& k, const L& ...args)
{
if(sel == k)
return 0;
int q = findarg(sel, args...);
return q >= 0 ? q + 1 : -1;
}
|
|
|
Re: get_i [message #54262 is a reply to message #54259] |
Wed, 17 June 2020 06:39   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
Novo wrote on Tue, 16 June 2020 15:42mirek wrote on Tue, 16 June 2020 15:25Novo wrote on Tue, 16 June 2020 21:22Another implementation using initializer_list:
template <typename T>
constexpr T get_i(int i, std::initializer_list<T> list)
{
return list[clamp(i, 0, list.size())];
}
This one is trictly-typed, although I couldn't check assembly with godbolt because it complains about something ...
Nah, we do not want strict typing here.
Sorry, last one won't compile.
The one using variadic template is fine, although it still needs specialization for const char* ... :-/
Fixed version. No performance degradation.
template <typename T>
constexpr T get_i2(int i, const std::initializer_list<T>& list)
{
const int n = list.size();
return *(list.begin() + clamp(i, 0, n));
}
const char* c = get_i2(1, {"zero", "one", "two"});
Assembler:
.L.str:
.asciz "one"
c:
.quad .L.str
Regards,
Novo
|
|
|
Re: get_i [message #54263 is a reply to message #54261] |
Wed, 17 June 2020 07:01   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
mirek wrote on Tue, 16 June 2020 18:01
Do you see any problems?
int ind = findarg(1, "0", 1.5, 2, 3);
RDUMP(ind);
ind = 1 in my case ...
Regards,
Novo
|
|
|
Re: get_i [message #54264 is a reply to message #54263] |
Wed, 17 June 2020 07:09   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
Novo wrote on Wed, 17 June 2020 01:01mirek wrote on Tue, 16 June 2020 18:01
Do you see any problems?
int ind = findarg(1, "0", 1.5, 2, 3);
RDUMP(ind);
ind = 1 in my case ...
Sorry, I was testing against current implementation in U++ again.
New implementation is fine.
Regards,
Novo
|
|
|
Re: get_i [message #54265 is a reply to message #54264] |
Wed, 17 June 2020 07:23   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
Well, it is "fine" because "findarg(1, "0", 1.5, 2, 3)" won't compile ...
But if you need a heterogeneous set of arguments, then you need to implement it differently ...
Regards,
Novo
|
|
|
Re: get_i [message #54266 is a reply to message #54265] |
Wed, 17 June 2020 07:50   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
Code below works for all data types, including const char*.
template <class T, class V>
constexpr auto decode(const T& sel, const V& def)
{
return def;
}
template <class T, class K, class V, typename... L>
constexpr auto decode(const T& sel, const K& k, const V& v, const L& ...args)
{
return sel == k ? v : decode(sel, args...);
}
Regards,
Novo
|
|
|
Re: get_i [message #54267 is a reply to message #54265] |
Wed, 17 June 2020 09:35   |
 |
mirek
Messages: 14255 Registered: November 2005
|
Ultimate Member |
|
|
Novo wrote on Wed, 17 June 2020 07:23Well, it is "fine" because "findarg(1, "0", 1.5, 2, 3)" won't compile ...
But if you need a heterogeneous set of arguments, then you need to implement it differently ...
Well, I really think above one should not compile... Heterogenous yes, but arguments must be comparable...
Mirek
|
|
|
Re: get_i [message #54268 is a reply to message #54267] |
Wed, 17 June 2020 13:03   |
 |
mirek
Messages: 14255 Registered: November 2005
|
Ultimate Member |
|
|
So I started looking into eliminating all instances of Expand macro usage and identified that following helpers could be quite useful:
template <class I, class V>
void iter_set(I t, V&& v)
{
*t++ = v;
}
template <class I, class V, typename... Args>
void iter_set(I t, V&& v, Args&& ...args)
{
*t++ = v;
iter_set(t, args...);
}
template <class C, typename... Args>
C gather(Args&& ...args)
{
C x;
x.SetCount(sizeof...(args));
iter_set(x.begin(), args...);
return x;
}
template <class I, class V>
void iter_get(I s, V& v)
{
v = *s++;
}
template <class I, class V, typename... Args>
void iter_get(I s, V& v, Args& ...args)
{
v = *s++;
iter_get(s, args...);
}
template <class C, typename... Args>
int scatter(int n, const C& c, Args& ...args)
{
if(n < sizeof...(args))
return 0;
iter_get(c.begin(), args...);
return sizeof...(args);
}
template <class C, typename... Args>
int scatter(const C& c, Args& ...args)
{
return scatter(c.GetCount(), c, args...);
}
Usage example:
template <typename... Args>
String Format(const char *fmt, const Args& ...args)
{
return Format(fmt, gather<Vector<Value>>(args...));
}
But I guess this would work even better if containers interface was amended to be more "std" (Vector::Vector(int count), size() synonyme for GetCount), so I guess that needs a bit more work...
Mirek
|
|
|
Re: get_i [message #54271 is a reply to message #54268] |
Wed, 17 June 2020 19:00   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
A fix:
template <class I, class V>
void iter_set(I t, V&& v)
{
*t++ = std::forward<V>(v);
}
template <class I, class V, typename... Args>
void iter_set(I t, V&& v, Args&& ...args)
{
*t++ = std::forward<V>(v);
iter_set(t, std::forward<Args>(args)...);
}
template <class C, typename... Args>
C gather(Args&& ...args)
{
C x;
x.SetCount(sizeof...(args));
iter_set(x.begin(), std::forward<Args>(args)...);
return x;
}
template <typename... Args>
String Format(const char *fmt, Args&& ...args)
{
return Format(fmt, gather<Vector<Value>>(std::forward<Args>(args)...));
}
Regards,
Novo
|
|
|
Re: get_i [message #54272 is a reply to message #54271] |
Wed, 17 June 2020 19:14   |
Novo
Messages: 1430 Registered: December 2006
|
Ultimate Contributor |
|
|
Another fix to avoid extra-copying ...
template <class I, class V>
void iter_set(I& t, V&& v)
{
*t = std::forward<V>(v);
}
template <class I, class V, typename... Args>
void iter_set(I& t, V&& v, Args&& ...args)
{
*t++ = std::forward<V>(v);
iter_set(t, std::forward<Args>(args)...);
}
template <class C, typename... Args>
C gather(Args&& ...args)
{
C x;
x.SetCount(sizeof...(args));
auto iter = x.Begin();
iter_set(iter, std::forward<Args>(args)...);
return x;
}
Regards,
Novo
|
|
|
Goto Forum:
Current Time: Tue Apr 29 15:59:47 CEST 2025
Total time taken to generate the page: 0.01415 seconds
|