To be fair, this sort of thing doesn't have to be so much worse in C++ (yes, it would have been nice if it had been built into the language itself to begin with). You just need a function to do a back-and-forth conversion which then double-check the results, ie:<p><pre><code> #include <exception>
#include <sstream>
template <typename From, typename To>
void convert_safely_helper_(From const& value, To& result) {
std::stringstream sst;
sst << value;
sst >> result;
}
// Doesn't throw, just fails
template <typename From, typename To>
bool convert_safely(From const& value, To* result) {
From check;
convert_safely_helper_(value, *result);
convert_safely_helper_(*result, check);
if (check != value) {
*result = To();
return false;
}
return true;
}
// Throws on error
template <typename To, typename From>
To convert_safely(From const& value) {
To result;
if (!convert_safely(value, &result))
throw std::logic_error("invalid conversion");
return result;
}
#include <iostream>
template <typename Buy, typename Quantity, typename Price>
void sendOrder(const char* symbol, Buy buy, Quantity quantity, Price price) {
std::cout << symbol << " " << convert_safely<bool>(buy) << " "
<< convert_safely<unsigned>(quantity) << " " << convert_safely<double>(price)
<< std::endl;
}
#define DISPLAY(expression) \
std::cout << #expression << ": "; \
expression
template <typename Function>
void test(Function attempt) {
try {
attempt();
} catch (const std::exception& error) {
std::cout << "[Error: " << error.what() << "]" << std::endl;
}
}
int main(void) {
test([&] { DISPLAY(sendOrder("GOOG", true, 100, 1000.0)); });
test([&] { DISPLAY(sendOrder("GOOG", true, 100.0, 1000)); });
test([&] { DISPLAY(sendOrder("GOOG", true, -100, 1000)); });
test([&] { DISPLAY(sendOrder("GOOG", true, 100.5, 1000)); });
test([&] { DISPLAY(sendOrder("GOOG", 2, 100, 1000)); });
}
</code></pre>
Output:<p><pre><code> sendOrder("GOOG", true, 100, 1000.0): GOOG 1 100 1000
sendOrder("GOOG", true, 100.0, 1000): GOOG 1 100 1000
sendOrder("GOOG", true, -100, 1000): GOOG 1 [Error: invalid conversion]
sendOrder("GOOG", true, 100.5, 1000): GOOG 1 [Error: invalid conversion]
sendOrder("GOOG", 2, 100, 1000): GOOG [Error: invalid conversion]
</code></pre>
Rust of course leaves "less footguns laying around", but I still prefer to use C++ if I have my druthers.