top | item 23629246

(no title)

andrewg | 5 years ago

C++17 does - it's called std::variant.

  std::variant<int, bool, double> options;
  options = true;
  
  bool value = std::get<bool>(options);
  bool has_bool = std::holds_alternative<bool>(options);
  
  // or test which alternative is held
  if (auto i = std::get_if<int>(&options)) {
    // do something with int
  } else if (auto b = std::get_if<bool>(&options)) {
    // do something with bool
  } else {
    // do something with double
  }

discuss

order

nicoburns|5 years ago

That's not a language feature though, right? It's just a struct containing a union and a discriminant. That means:

- No pattern matching means your stuck with the awkward if-elseif-else

- It doesn't check that you've accounted for every possible variant.

- You can only hold one variant of each type: you can't have two variants that both contain a string.

- The specific instance isn't it's own type, so you can't implement methods on it.

etalian|5 years ago

A language that I am working on has those things "built-in" (it still generates compilable C++ in the backend). I did recently add pattern matching (and not just on variants) where the above would be equivalent to:

  $v:|[:int, :bool, :double] = 5;

  $vi: = v.[:int];
  $holds_int: = v.?[:int];

  switch v {
    .[:int]$i { /std cout << "got int:" << i };
    .[:bool] { /std cout << "got bool" };      
    .[:double]$d { /std cout << "got double: " << d };
  };
If a type repeats in the same variant, you would match by index to tell them apart:

  $v:|[:int, :int]<(.[0] = 5);

  switch v {
    .[0]$i0 { /std cout << "got first int:" << i0 };
    .[1]$i1 { /std cout << "got second int: " << i1 };
  };
(I have some ideas to allow named labels instead of numerical indices but that is not yet implemented)

It is, in spirit, an implementation of the inspect proposal [1], but with a (subjectively) much simpler and more powerful syntax (the grammar of the entire language is fully LALR(1) without ambiguities).

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p137...

TFortunato|5 years ago

Take a look at my reply above. Your right it isn't built into the language, but it does come with a helper method, std::visit, which does take into account that you've accounted for all possible variant types, and will throw a compile error if you didn't. You also aren't stuck with if, else-if style syntax.

pjmlp|5 years ago

You can use std::visit() for the time being, and pattern matching is being discussed.

TFortunato|5 years ago

This works out really nicely when combined with variadic template tricks. (Taken from CPP Reference)

  // Helper for creating anonymous visitor functions
  template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
  template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

  using var_t = std::variant<int, long, double, std::string>;

  std::vector<var_t> vec = {10, 15l, 1.5, "hello"};

  // Type matching visitor
  for (auto& v: vec) {
        std::visit(overloaded {
            [](auto arg) { std::cout << arg << ' '; },
            [](double arg) { std::cout << std::fixed << arg << ' '; },
            [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
        }, v);
    }
Outputs: 10 15 1.500000 "hello"

zeotroph|5 years ago

That does not give you destructuring though, so to distinguish different variants of the same type you are left with a classic `if` inside the lambda.

Also this has "language support" in the sense that it is (again) implemented via template meta programming, i.e. the compile time is impacted quite heavily.

See "std::visit is everything wrong with modern C++": https://bitbashing.io/std-visit.html

This standards proposal would add proper match (named, of course, inspect) support: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p009...

dunefox|5 years ago

What a ridiculous language C++ is.