Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GENERATE() changes compile time length of arrays #2012

Closed
SmorkalovG opened this issue Aug 31, 2020 · 3 comments · Fixed by #2058
Closed

GENERATE() changes compile time length of arrays #2012

SmorkalovG opened this issue Aug 31, 2020 · 3 comments · Fixed by #2058

Comments

@SmorkalovG
Copy link

Description
GENERATE macro takes type of first argument and cast all other args to it. It is ok for most cases but for string literals it is bad. It takes length of first literal and force it for all.
TEST_CASE("t", "[t]") { auto& str = GENERATE("123", "1"); WARN(typeid(str).name()); }
Output
warning: A4_c
Usually you don't see it. If you use std::string to store the result of GENERATE then everything works fine, all lengths are correct. But some string implementations use length from type of array. E.g., https://stackoverflow.com/questions/2384107/magic-arguments-in-function-templates
This template magic works wrong with GENERATE. I cann't say this is a bug, because I have no idea how to fix it. May be detect arrays and generate compilation error if lengths are different?

It is very bad rule in C++ that references to arrays with different lengths can be cast to each other.

@horenmar
Copy link
Member

horenmar commented Sep 5, 2020

Right, so, there are two parts to this.

  1. single GENERATE "call" can have only one return type. (even though for e.g. TEMPLATE_TEST_CASE, the type can differ across different input types...)
  2. deducing string literals as char arrays with specific size can be a problem.

I cannot change 1), and fixing 2) by forcing a pointer decay would break people who already use it with all literals having the same size.

I will have to think about whether there is an use case for having different size string literals deduce as the same size, and if not, static_assert that they share length is indeed the best option.

In the meantime, if you have this issue in your code, consider passing as<char const*>{} as the first element in GENERATE. Doing so forces the type to be deduced as char const*.

@SmorkalovG
Copy link
Author

Thank you for answer.
Yes, casting to string is how I solved this problem in my case.

For general case there is the only solution - detect reference to array. It is a question what to do with it. One option is static assert for length, but it is code breakage. If somebody used std::string a = GENERATE("a", "ab"); it worked well and, imho, should continue working. But static_assert breaks it.
Another option is cast to const char*. Correct code with std::string continues to compile and incorrect depends on situation. If it could not work without length then it wouldn't compile. And it is good because it was wrong code that worked with wrong length. If it can work with const char* instead of char&[N] then everything is OK like std::string.

Detection itself can be implemented with function overload, e.g., template<typename T,size_t N> void generate(T (&a)[N])

@horenmar
Copy link
Member

Update: I've decided that for v3 I am going to make arrays decay. Not sure if I will port this to v2 as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants