ForgottenCat's blog

By ForgottenCat, history, 3 months ago, In English

We will use __COUNTER__ and a #define, so it would eventually duplicates given parameter N times. I can't give you a macro that just REPEAT(100, abcd) and will expand to 100*abcd without defining 100 lines of macro. If you don't want to see how it works, scroll down to the bottom.

Firstly, let's see this technique:

#define EXP(...) EXP1(EXP1(EXP1(EXP1(__VA_ARGS__))))
#define EXP1(...) EXP2(EXP2(EXP2(EXP2(__VA_ARGS__))))
#define EXP2(...) EXP3(EXP3(EXP3(EXP3(__VA_ARGS__))))
#define EXP3(...) EXP4(EXP4(EXP4(EXP4(__VA_ARGS__))))
#define EXP4(...) __VA_ARGS__ // Just expand it lots of times.

#define REP() 1 DEFER(REP_I)()()
#define EMPTY()
#define DEFER(...) __VA_ARGS__ EMPTY()
#define REP_I() REP

EXP(REP())

If you expand the EXP(REP()), you will see a lot of 1. Why? Because the macro DEFER() make it so that REP can be expanded many times within a single macro. Just like void dfs() { cout << 1; dfs();}. But this also has an end: it depends on how many time you expand it. That's why we added lots of EXP(). You can even add EXP5 and more, but you'll probably start lagging. Also you can try expand it for 1 or 2 times to see the difference.

Also that's how the SP() macro made in my template.

But how to make it stop? We can use __COUNTER__. After certain times of expansion, the __COUNTER__ will reach a certain value, then we can make it stop.

#include <bits/stdc++.h>
using namespace std;
#define EXP(...) EXP1(EXP1(EXP1(EXP1(__VA_ARGS__))))
#define EXP1(...) EXP2(EXP2(EXP2(EXP2(__VA_ARGS__))))
#define EXP2(...) EXP3(EXP3(EXP3(EXP3(__VA_ARGS__))))
#define EXP3(...) EXP4(EXP4(EXP4(EXP4(__VA_ARGS__))))
#define EXP4(...) __VA_ARGS__

#define EMPTY(...)
#define DEFER(...) __VA_ARGS__ EMPTY()
#define CONCAT(A, B) CCONCAT(A, B)
#define CCONCAT(A, B) A##B
#define SECO(A, B, ...) B
#define BASE(B) , DEFER(REP_I)()(__COUNTER__, B)
#define REP(A, B) B DEFER(SECO)(CONCAT(REP_, A), BASE)(B)
#define REP_I() REP

#define rep(A) EXP(REP(__COUNTER__, A))

#define REP_29 A, EMPTY
#define REP_59 A, EMPTY
#define REP_99 A, EMPTY

int a[]{rep(1)};
int b[]{rep(1)};
int c[]{rep(3)};

int main()
{
	cout << size(a) << endl; // 30
	cout << size(b) << endl; // 30
	cout << size(c);         // 40
}

To use this, assume current __COUNTER__ value is x, then #define REP_X+N-1 A, EMPTY, so the given macro A will expand N times. Then x will add N.

To customize separator, see the part: #define BASE(B) , and change the comma to anything else.

If you see it expand to something else at the end, you need to add more EXP. Currently it can repeat at most 171 times using one rep. Change REP_99 to REP_230 and REP_231 to see.

Full text and comments »

  • Vote: I like it
  • +3
  • Vote: I do not like it

By ForgottenCat, history, 3 months ago, In English

I use vector::assign_range, and CE. Also happens on append_range (using C++23). Is the compiler outdated? If so, plz update.

Full text and comments »

  • Vote: I like it
  • +8
  • Vote: I do not like it

By ForgottenCat, history, 3 months ago, In English

So many people want to make a template that including everything needed to code faster during rounds. But if your templates are too long, it will take some time to scroll down. So here's a solution (asides from #pragma region): #include __FILE__.

Combine it with #ifdef and other defines, it can move codes to the top section. Here's an example:

#ifndef TYPE_A
#define TYPE_A 1
#include __FILE__
int main()
{
    lint a=0;
}
#elif TYPE_A == 1
using lint=long long;
#endif

You can see it just move the lint declaration to the top. But your clangd might give you an error. To bypass this, you need to add a using before the #include. It can be using namespace ..., or using lint = int64_t. But I suggest you to write like this:

#ifndef TYPE_A
#define TYPE_A 1
#include <bits/stdc++.h>
using namespace std;
#include __FILE__
int main()
{
    lint a=0;
}
#elif TYPE_A == 1
using lint=long long;
#endif

Now there will be no warnings or errors anymore. Also if you decided to move bits/stdc++.h down below, your clangd will be super slow.

Also you can #include __FILE__ multiple times, just #undef TYPE_A the redefine it. But that will make your template a terrible chaos. Use at your own risk.

I've been using this technique on several OJs, they can all accept such codes.

Note that even if clangd gives you an error, g++ can still compile this.

Full text and comments »

  • Vote: I like it
  • +12
  • Vote: I do not like it

By ForgottenCat, history, 5 months ago, In English

1.The VA_ARGS traverse define, which only takes $$$O(logN)$$$ lines.

Normally to do this, you need to:

#define SP1(A) ...
#define SP2(A,B) ...
#define SP3(A,B,C) ...
...

Which is $$$O(N)$$$ defines. Here's an example supporting 31 or 32 (I forget) args using only $$$log32+5$$$ defines. Another 2 used for declaring a tool.

// O(logN) lines of __VA_ARGS__ traverse
#define SIZEOF_ARGS_H(...)SSIZEOF_ARGS_H(__VA_ARGS__,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,8,8,8,8,8,8,8,8,4,4,4,4,2,2,1)
#define SSIZEOF_ARGS_H($2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,N,...)N
#define CONCAT(a,b) CCONCAT(a,b)
#define CCONCAT(a,b) a##b
#define SP(A,B,...)CONCAT(SP,SIZEOF_ARGS_H(__VA_ARGS__))(A,B,__VA_ARGS__)
#define SP1(A,B,C)A(B,C)
#define SP2(A,B,C,D,...)A(B,C)A(B,D)__VA_OPT__(A(B,__VA_ARGS__))
#define SP4(A,B,C1,C2,C3,C4,...)SP2(A,B,C1,C2)SP2(A,B,C3,C4)__VA_OPT__(CONCAT(SP,SIZEOF_ARGS_H(__VA_ARGS__))(A,B,__VA_ARGS__))
#define SP8(A,B,C1,C2,C3,C4,C5,C6,C7,C8,...)SP4(A,B,C1,C2,C3,C4)SP4(A,B,C5,C6,C7,C8)__VA_OPT__(CONCAT(SP,SIZEOF_ARGS_H(__VA_ARGS__))(A,B,__VA_ARGS__))
#define SP16(A,B,C1,C2,C3,C4,C5,C6,C7,C8,C9,C10,C11,C12,C13,C14,C15,C16,...)SP8(A,B,C1,C2,C3,C4,C5,C6,C7,C8)SP8(A,B,C9,C10,C11,C12,C13,C14,C15,C16)__VA_OPT__(CONCAT(SP,SIZEOF_ARGS_H(__VA_ARGS__))(A,B,__VA_ARGS__))
// Classic usage of SP, to generate refs for struct members.
#define REFER(A,B)auto&B=A.B;
#define refer(A,...)SP(REFER,A,__VA_ARGS__)
// up to 31 or 32 Args available.

struct s{int a,b,c,d,e,f;}g;

int main(){
    refer(g,a,b,c,d,e,f);
    // expand to auto&a=g.a;auto&b=g.b;auto&c=g.c;......
    a=0,b=0,c=0,d=0,e=0,f=0;
}

2. Auto min max

You need at least C++14 to use this.

The min max that can automatically infer the type of return value using common_type.

template<class a,size_t I=0,typename=void>struct d${using type=common_type_t<decltype(get<I>(declval<a>())),typename d$<a,I+1>::type>;};template<class a,size_t I>struct d$<a,I,enable_if_t<I==tuple_size<decay_t<a>>::value-1>>{using type=decltype(get<I>(declval<a>()));};
#define t$(A)[&]<size_t... I>(auto &u,index_sequence<I...>){typename d$<decltype(u)>::type ma=get<0>(u);((ma=A<decltype(ma)>(ma,get<I>(u))),...);return ma;}
#define min(...)({auto $$$=make_tuple(__VA_ARGS__);t$$$(min)($$$,make_index_sequence \lt tuple_size \lt decltype($$$)>::value>{});})
#define max(...)({auto $$$=make_tuple(__VA_ARGS__);t$$$(max)($$$,make_index_sequence \lt tuple_size \lt decltype($$$)>::value>{});})

The downside is it will call min(A, A) for once. For example min(1,2,3) will be min(min(min(1,1),2),3). Maybe it can be solved cleaner by using SP().

Note that common_type_t<A, A> == A. You can even put strings there as long as they're comparable.

Supports infinite args. After formatting it would look like this.

template<class a, size_t I = 0, typename = void>
struct d$
{
	using type = common_type_t<decltype(get<I>(declval<a>())), typename d$<a, I + 1>::type>;
};
template<class a, size_t I>
struct d$<a, I, enable_if_t<I == tuple_size<decay_t<a>>::value - 1>>
{
	using type = decltype(get<I>(declval<a>()));
};
#define t$(A) \
	[&]<size_t... I>(auto &u, index_sequence<I...>) { \
		typename d$<decltype(u)>::type ma = get<0>(u); \
		((ma = A<decltype(ma)>(ma, get<I>(u))), ...); \
		return ma; \
	}
#define min(...) \
	({ \
		auto $ = make_tuple(__VA_ARGS__); \
		t$(min)($$$, make_index_sequence \lt tuple_size \lt decltype($$$)>::value>{}); \
	})
#define max(...) \
	({ \
		auto $ = make_tuple(__VA_ARGS__); \
		t$(max)($$$, make_index_sequence \lt tuple_size \lt decltype($$$)>::value>{}); \
	})

Example usages:

auto a=min(1,2ll,3.0f);
// decltype(a) == float, a=1.0f

auto b=min(string("1"),string("3"));
// b==string("1");

By the way I think there's space left for optimizing, I'm not a professional STL user, cannot figure out how tf does index_sequence come to work. I have been using these at least two months, so there's little chance that they have secret bugs.

Full text and comments »

  • Vote: I like it
  • +2
  • Vote: I do not like it