←back to thread

611 points LorenDB | 1 comments | | HN request time: 0s | source
Show context
spyrja ◴[] No.43910130[source]
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:

  #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)); });
  }

Output:

  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]

Rust of course leaves "less footguns laying around", but I still prefer to use C++ if I have my druthers.
replies(1): >>43912644 #
pjmlp ◴[] No.43912644[source]
Yes, from safety point of view Rust is much better option, however from the ecosystems I care about (language runtimes and GPU coding), both professionally and as hobby, C++ is the systems language to go, using Rust in such contexts would require me to introduce extra layers and do yak shaving instead of the actual problem that I want to code for.
replies(1): >>43921048 #
spyrja ◴[] No.43921048[source]
Well, precisely. Such is the price of "general-purpose safety". The biggest irony IMO is that the security features of Rust are so often skirted in the name of speed, efficiency, etc, with the end result being a program which isn't much more secure than its C++ equivalent. ¯\_(ツ)_/¯
replies(2): >>43921386 #>>43923359 #
1. pjmlp ◴[] No.43923359{3}[source]
It isn't the safety or lack thereof, rather really adding another toolchain, development and debugging complexity.

Lets say I am doing something with V8, CLR, JVM, GCC, LLVM, CUDA, Metal, outside the managed languages that sit on top of those runtimes, compiler toolchains, GPGPU.

That infrastructure code is written in C++, and making use of Rust means adding yet another layer on how to compile, interop and debug, on top of what is already there.

As for safety in general, I am a believer of systems programming languages with automatic memory management, and polyglot programming, we aren't yet there because industry has the tendency to take decades to finally adopt good ideas, and only on a partial way.

Thus while I appreciate what Rust has achieved bringing affine types into mainstream, not an easy job, I consider this a transition step until we get both approaches in the same language, automatic resource management with affine,linear,effects,dependent types.

However this ultimately won't matter as much, when we eventually get our AI buddies good enough that they can spew executables directly.