Godbolt updated.
Release 0.6.0
For Visual Studio users pompously named epiphany.h
is added. Idea is to turn some quiet implicit conversion warnings into errors. Just enough to make your code not compilable without the help of nothing_but<T>
. Also
method and assignment methods got their ref. qualified variants. In order to stop taking data from temporary objects and to stop assigning to temporary objects.data()
Release 0.5.0
GitHub: Value Handle to Avoid Implicit Conversions in standard C++. This is the official and latest version: “0.5.0”. The major change is, “0.5.0” and tests over there, are made for runtimes where this utility will be most likely used. That means no exceptions, no RTTI, no streams. Please use that code and tests as a reference.
Motivation
Probably you indulge in recreational C++ and the following is just a curiosity for you:
1 2 3 4 5 6 7 8 9 10 11 |
// https://godbolt.org/z/vov5dYdc4 #include <inttypes.h> // CLANG/GNUC/G++/MSVC/UCRT default behaviour // is : no warnings whatsoever here // assign signed to unsigned char uint8_t uc = int8_t('s'); // assigned unsigned to signed char int8_t sc = uc; unsigned char s_uc = uc ; signed char s_sc = uc ; signed char sc_2 = 2 ; |
Or you do take the above so seriously you actually do not use C++ on a mission-critical project? Perhaps inside some medical equipment, your company has to deliver?
You might take the C++ implicit conversions so seriously that “even” the following is a very serious matter for you.
1 2 3 4 |
// implicit conversion of int to char char C = 64 ; // implicit conversion of double to float float F = 3.7; |
Not because you happen to be “unreasonable”, but because you need to deliver code where implicit conversion is simply not allowed. Just like for example exceptions, in many real-time projects are not allowed. They simply do not exist over there. Simply switched off.
And very likely, you have turned to the “official sources” just to come back disappointed.
One option is to plan for the usual extended test/debug/test/debug … ad infinitum cycling, of course.
The suggestion
Before you discard C++ (with the heavy heart) for mission-critical projects completely, we might suggest you look into this ridiculously tiny single header? Here is some code to tickle your fancy.
1 2 3 4 5 |
// let's assume your code must not accidentally mix // signed and unsigned chars // using our tiny lib declare the types you need using signed_char_type = dbj::util::nothing_but< char >; using unsigned_char_type = dbj::util::nothing_but< unsigned char >; |
Declarations first. As ever, this makes the default initialized content, but with a twist: of exactly the types required.
1 2 3 |
// declarations signed_char_type signed_char; unsigned_char_type unsigned_char; |
Usage? That is the interesting part. To actually assign anything to these types you must very consciously make those types first. The act of assignment is not transparent. Thus assignment does not hide the implicit conversion.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// this compiles OK // we are casting to the required char type before // the assignment signed_char = char('s'); // also compiles OK // we need exactly unsigned char // so that we can assign to the variable u // in the real life situation the code bellow // might be several hundred lines from the // declarations above // just_unsigned u; using uchar = unsigned char; unsigned_char = uchar('u'); |
You or your team simply can not introduce a bug there. The following will simply not compile.
1 2 3 4 |
signed_char = 's'; // char to signed char, does not compile unsigned_char = 'u'; // does not compile signed_char = u; // unsigned to signed char does not compile signed_char == u; // can not compare signed and unsigned, thus this does not compile |
Just a perfect API to avoid those nasty little pests growing into bugs very difficult to find. And software where implicit conversions are a problem definitely can not afford those bugs. Or any bugs.
Great, is that it? Are we done here yet? No, it is not it. And we are not done here yet. There is more.
In case you might think the only simple implicit conversion of fundamental types, can be targeted with this API, how about something a bit more involved?
Non-Trivial Use-Case
Let’s assume your C++, for the heart monitor module at one point needs exactly an array of 3 integers. Aka int three_ints[3] = {1,2,3};
But. Not a “naturally decayed” pointer and not unsigned int
type of those elements, but exactly as the MISRA based requirement requested: an array of three integers, and nothing else but an array of three integers.
Now, I am sure if really pressed you can devise some clever C++, ad-hoc solution. Provided you are allowed to use them “clever solutions” in your heart monitoring device module. Which I am sure you are definitely not. Ugh.
Alternatively, perhaps we can interest you in the following snippet?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// using 'dbj nothing but' API // NOTE! signed int is the default int type using just_int = dbj::util::nothing_but< int >; // here is the safe and sound array of // nothing but 3 signed ints using just_int_3 = just_int[3]; // just keep it in the scope please just_int_3 safe_arr{ 1, 2, 3 }; // in case you are wondering this works too // vector of nothing_but<T> auto tv = std::vector<just_int>( ); // of course 42 is "signed int" // so this works tv.push_back(42); // but. can not mix types accidentaly // char 'X' might be coerced to its signed // int representation, no worries here // does not compile: tv.push_back('X') ; |
Please remember the elements of that vector are instances of nothing_but<T>
, thus you need to use the data method to get to the values.
1 2 3 4 |
// this is instance of nothing_but<T> auto first = tv[ 0 ]; // use the data method to get to the value std::cout << first.data(); |
Now, this might seem like a “not a lot of code” to you, but we are just showing an API new to you. It can be used in a much more condensed manner.
The above code looks almost too simple. It is easy to forget the safety service this API provides. Now the really worn-out phrase:
Your imagination is the limit.
Very true this time and here, with this API.
Type’s handled
We do handle all the arithmetic types.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// integral types - bool - char - char16_t - char32_t - wchar_t - signed char - short int - int - long int - long long int - unsigned char - unsigned short int - unsigned int - unsigned long int - unsigned long long int // floating point types - float - double - long double |
Those are the types where implicit conversions do happen, by default. But what about compound types? For example:
1 2 3 |
- References - Pointers - Arrays |
Why not handle them too? Simply because in case you need them you will naturally use them as ever before, made up of fundamental types, and combined with this API.
1 2 3 4 5 6 7 8 9 10 11 |
using just_signed = dbj::util::nothing_but< char >; just_signed signed_char; // pointer to it; pointer is a compound type just_signed * signed_char_pointer; // null_ptr as ever // reference to it just_signed & signed_char_ref = signed_char; // array of three elements of it just_signed signed_char_arr[3]; |
Please do note, how above, all the standard C++ default value initialization rules are respected. In case of some serious bugs, singularities discovered, or edge cases, we will reconsider the currently handled types. Going beyond arithmetics it is very unlikely the implicit conversion might be the problem.
Dependencies
- This API depends on C++ std lib only.
- We are developing using the Visual Studio 2017 15.9.X,
- using clang-cl.exe aka “clang 10.0.0”
- We are always checking is it equally usable with both CLANG and GCC, by using Godbolt
Installation
This API is header-only: it consists entirely of one header file:
dbj_nothing_but.h
, repository: https://github.com/dbj-systems/nothingbut
Thus it is perfectly usable in your Godbolt experiments as it is in that link above.
No compilation is necessary. No installation is required.
Just make it part of your project. Every attempt has been made to make this into a cross-platform,
header only, standard C++ library.
License
Contact
Please report issues or questions here.
You can contact me via Twitter at @dbjdbj, or via dbj at dbj dot org.
Contributing
Any feedback from users and stakeholders will be reviewed and might be used to improve the library.