emil-e/rapidcheck

int8_t gets displayed as an ASCII character rather than as a number

CapacitorSet opened this issue · 1 comments

When my input function takes int8_t arguments, RapidCheck prints them as ASCII characters rather than as numbers; the same happens when comparing int8s in RC_ASSERT.

Specifically, my code goes like this:

TEST_F(Int8Test, Sum) {
	::rc::detail::checkGTest([=](int8_t plaintext_a, int8_t plaintext_b) {
		auto a = Int8(plaintext_a, clientParams);
		auto b = Int8(plaintext_b, clientParams);
		auto sum = Int8(serverParams);
		sum.add(a, b);
		RC_ASSERT(sum.toI8(clientParams) == plaintext_a + plaintext_b);
	});
}

The error goes like "Falsifiable after 96 tests and 5 shrinks: std::tuple<signed char, signed char>: (�, �)" rather than eg. std::tuple (123, 45). Likewise, for the assertion I'm getting "Expands to: � == 128" rather than eg. "Expands to: 123 == 128".

(My terminal displays those as unicode replacement characters.)

This issue isn't a rapidcheck issue, it's a compiler-specific 8-bit integral type implementation issue. Both gcc and clang use a set of typedefs to provide all of the sized integral types to user code. On my machine, for example:

#include <iostream>
#include <type_traits>

int main() {
  std::cout << std::is_same<signed char, int8_t>::value << std::endl;
  std::cout << std::is_same<char, uint8_t>::value << std::endl;
  std::cout << std::is_same<short int, int16_t>::value << std::endl;
  std::cout << std::is_same<int, int32_t>::value << std::endl;
  std::cout << std::is_same<long int, int64_t>::value << std::endl;
  return 0;
}

will print

1
1
1
1
1

The issue is that the typedef means the compiler will select function overloads for the type being aliased, so the stream operator you get is the stream operator for signed char. If you provide a stream operator for the alias type, the compiler will prefer that overload to the aliased type's operator, and if you prefix any integral type with + it will force it to stream the numeric value instead of the char. Putting that all together, if you include the following in a header, which you include before rapidcheck is included, then it should find your custom stream operator for 8-bit integral types:

#include <iostream>

std::ostream& operator<<(std::ostream& os, const int8_t& value) { return os << +value; }
std::ostream& operator<<(std::ostream& os, const uint8_t& value) { return os << +value; }

Seeing as this isn't a bug in rapidcheck, I think this issue can be closed.