Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
Bazaar
Status & Roadmap
FAQ
Authors & License
Forums
Funding Ultimate++
Search on this site
Search in forums












SourceForge.net Logo
Home » Community » U++ community news and announcements » get_i
get_i [message #54247] Sun, 14 June 2020 19:10 Go to next message
mirek is currently offline  mirek
Messages: 12581
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 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced 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 #54252 is a reply to message #54250] Tue, 16 June 2020 17:52 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 12581
Registered: November 2005
Ultimate Member
I am not 100% if above is an experiment or suggestion to change things.

If later, then the obvious counterargument is that what I wrote is just demonstration, in real life the expression will not be constant... Smile

Mirek
Re: get_i [message #54253 is a reply to message #54252] Tue, 16 June 2020 18:02 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced Contributor
mirek wrote on Tue, 16 June 2020 11:52
I am not 100% if above is an experiment or suggestion to change things.

If later, then the obvious counterargument is that what I wrote is just demonstration, in real life the expression will not be constant... Smile

Mirek

constexpr behaves as normal function when expression is not a constant ...
So, it will work.
It is a suggestion to change things ...
It is a suggestion to use constexpr ...


Regards,
Novo
Re: get_i [message #54254 is a reply to message #54253] Tue, 16 June 2020 18:21 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced 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 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 12581
Registered: November 2005
Ultimate Member
Novo wrote on Tue, 16 June 2020 18:21
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 ...


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 Smile

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 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced 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 #54258 is a reply to message #54257] Tue, 16 June 2020 21:25 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 12581
Registered: November 2005
Ultimate Member
Novo wrote on Tue, 16 June 2020 21:22
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 ...


Nah, we do not want strict typing here.
Re: get_i [message #54259 is a reply to message #54258] Tue, 16 June 2020 21:42 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced Contributor
mirek wrote on Tue, 16 June 2020 15:25
Novo wrote on Tue, 16 June 2020 21:22
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 ...


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 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced 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 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 12581
Registered: November 2005
Ultimate Member
mirek wrote on Tue, 16 June 2020 21:20
Novo wrote on Tue, 16 June 2020 18:21
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 ...


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 Smile


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 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced Contributor
Novo wrote on Tue, 16 June 2020 15:42
mirek wrote on Tue, 16 June 2020 15:25
Novo wrote on Tue, 16 June 2020 21:22
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 ...


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 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced 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 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced Contributor
Novo wrote on Wed, 17 June 2020 01:01
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 ...

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 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced 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 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced 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 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 12581
Registered: November 2005
Ultimate Member
Novo wrote on Wed, 17 June 2020 07:23
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 ...


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 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 12581
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 Go to previous messageGo to next message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced 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 Go to previous messageGo to previous message
Novo is currently offline  Novo
Messages: 1048
Registered: December 2006
Experienced 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
Previous Topic: SIMD encapsulation
Next Topic: 2020.1 officially released
Goto Forum:
  


Current Time: Wed Jul 15 03:48:21 CEST 2020

Total time taken to generate the page: 0.01300 seconds