Have you ever hoped that C++ would have digit separators? That you wouldn’t have to strain your eyes when reading 2147483647 (is it std::numeric_limits<int32_t>::max(), or is it just similar)? That you wouldn’t have to count the zeros 5 times when typing 1000000000?
Well, the C++ Standards Committee doesn’t have your back. Oh, sure, they have introduced a digit separator, – ‘, but it’s completely unusable in production code! Here’s why.
Let’s say you’re writing a new feature:
struct answer_provider { static constexpr auto answer() noexcept(true) { return 42'042; } }; |
As you can see, my blog’s parser does not handle this separator correctly, and I assure you, neither do many other parsers! Effectively, this means that your new feature’s code will be displayed incorrectly in your organization’s code review tool. And obviously no (even seemingly) incorrect code can ever hope to pass a review.
For an another example, let’s consider the following code:
int main() { auto a = 8'8'; auto b = u8'8'; auto c = 8'8; auto d = u8'8; } |
What are the types of a, b, c and d? Hint: some of them are actually compiler errors, and you can’t rely on my blog’s syntax colouring.
And that’s why this digit separator is a complete failure.
If only there was a way to use _ as the separator, just like in D, Ruby, Python, C#, Julia, Ada, Rust and even Java…
A library solution!
Fear not, where the committee dropped the ball, I am picking it up! I created a great digit separator library. It’s very simple to use, just include the file:
#include <digit_separators.hpp> |
This will allow you to use proper digit separators trivially:
#include <iostream> #include <digit_separators.hpp> int main() { std::cout << 123_456_789 - 12_345_678 << "\n"; } |
> g++ foo.cpp -I../../data/include -std=c++17 && ./a.out 111111111 |
Great, isn’t it? Obviously. But there’s a small caveat: due to buggy compiler implementations, I had to split the implementation of digit separators for billions into separate files. Apparently, they can’t handle large files. Ridiculous, I know, but what can you do?
So, if you want to test larger numbers, you have to use additional headers (header numbers respective to millions):
#include <iostream> #include <limits> #include <cstdint> #include <digit_separators.hpp> #include <digit_separators_147.hpp> int main() { static_assert(std::numeric_limits<int32_t>::max() == 2_147_483_647); } |
> clang++ foo.cpp -I../../data/include -std=c++17 && ./a.out |
Of course compiles with no errors. What a success!
Where can I get it?
The library is rather large:
Unfortunately, GitHub, Bitbucket, GitLab and other git providers proved to be really backwards with their silly repository size limits, so I couldn’t share my library through their services. But you can download it here (full archive) and here (separate headers).
Additionally, here’s the ruby script I’ve used to generate the headers:
#!/usr/bin/ruby def gen_sep(values) name = values.map{ |v| '_%03d' % v }.join value = "#{1000 ** values.size}ull * value + "; value += values .reverse .each_with_index .map{ |v, i| '%3d * %dull' % [v, 1000**i] } .reverse .join(' + ') func = 'inline constexpr auto operator""%s(unsigned long long value){ return %s; }' func % [name, value] end def write_file(path, suffix, values, mode = 'w') final_path = File.join(File.realpath(path), 'digit_separators%s.hpp' % suffix) puts "Creating #{final_path}" File.open(final_path, mode) do |f| f.puts('#ifndef DIGIT_SEPARATORS%s_HPP' % suffix.upcase) f.puts('#define DIGIT_SEPARATORS%s_HPP' % suffix.upcase) values.each do |v| f.puts gen_sep(v) end f.puts('#endif // DIGIT_SEPARATORS%s_HPP' % suffix.upcase) end end exit if ARGV.size < 1 DEST_PATH = ARGV.first THOUSAND = (0...1000).to_a def write_main_file(path) to_create = THOUSAND.map{ |v| [v] } + THOUSAND.product(THOUSAND) write_file(DEST_PATH, '', to_create) end def write_nth_million(path, n) to_create = [n].product(THOUSAND, THOUSAND) write_file(DEST_PATH, '_%03d' % n, to_create) end write_main_file(DEST_PATH) THOUSAND.each do |n| write_nth_million(DEST_PATH, n) end |
Great job, bro! I’ll use it in all my projects!
Hi, thanks for providing such a great formatting tool, wondering if its possible to put it back in to service again ? http://format.krzaq.cc/ is now showing 502 bad gateway
Should be up and running now :) I’m happy you find it useful.
Great information. Since last week, I am gathering details about the c++ experience. There are some amazing details on your blog which I didn’t know. Thanks.
Rust uses single quotation mark as lifetime parameter, and most syntax highlighters learned to handle these correctly (I’ve seen some that still have problems with these, though).