renatoGarcia / icecream-cpp

🍦 Never use cout/printf to debug again
Other
555 stars 29 forks source link

Feature Request: Allow Printing Arrays of Data Types #52

Open wongsingfo opened 1 month ago

wongsingfo commented 1 month ago

Request: Add a new macro ICA(array, length) that can print the array.

Example Usage:

int a[100] = {1, 2, 3, 4};
ICA(a+1, 2); // prints "ic| (a+1)[2]: [2, 3]"

pair<int, int> b[100] = {make_pair(5, 6), make_pair(7, 8)};
ICA(b, 2); // prints "ic| (b)[2]: [(5, 6), (7, 8)]"

vector<int> c(100, 5);
ICA(c.begin()+10, 3); // prints "ic| (c.begin()+10)[3]: [5, 5, 5]"

Usefulness:

renatoGarcia commented 1 month ago

It is a really good feature idea! I however wouldn't like to add one more IC macro variant, because they compose multiplicatively (apply, configure, etc). I think this functionality would be better exposed as a formating option. Something in the lines of:

int a[100] = {1, 2, 3, 4};
IC_("[1:3]", a);

printing

ic| a[1: 3]: [2, 3]

where the formatting string would accept the same as a python range.

What do you think about that?

wongsingfo commented 1 month ago

Good point. However, my concern is that [1:3] in your example is hard-coded at compile time. Suppose a user wants to print the first n but no more than 10 elements (where n is a variable) of an array. For instance:

IC_("[0:min(n,10)]", a)

There's no way to get the value of min(n, 10) at runtime because it is a string used in compile time.

My suggestion is to use a string format to indicate that the following two arguments should serve as the pointer to the beginning of the array and the length of the array. For example, let's use [] to show we want to print an array. Then:

int a[100] = {1, 2, 3, 4};
IC_("[]", a+1, 2);

This will print:

ic| (a+1)[2] : [2, 3]
renatoGarcia commented 1 month ago

That string is not necessarily hardcoded at compile time. We could use something like

auto i = min(n, 10);
auto s = std::string{"[0:"}  + std::to_string(i) + "]";
IC_(s, a);

Yes, ugly as hell.

I see two problems with this approach at you example. One is if using a+1 to set the starting point, this solution would not to apply to a std::vector, for example. Ideally this range selector would apply to any indexable, or even iterable, class. And to keep the consistence, all the arguments of the IC macro should be the values to be printed. I think it is confusing changing one of them to a range argument, contingent on the value of a string.

But I agree that is helpful if we have a nice way to dynamically build the range string. I'm inclined to create a function like:

IC_(IC_FMT("[0:{}]", min(n,10)), a);