Remix.run Logo
moogly 2 days ago

Is this similar? https://github.com/amantinband/error-or

What's some of the "preprocessor magic" that makes this[1] more ergonomic to use?

[1]: https://github.com/abseil/abseil-cpp/blob/master/absl/status...

galkk 2 days ago | parent [-]

Look at the long chains of methods in https://github.com/amantinband/error-or?tab=readme-ov-file#n.... The code looks quite unnatural.

In google/c++ you can do much simpler, but with preprocessor magic.

Example:

    absl::StatusOr<User> loadUserById(int userId) { ... }

    absl::Status populateItems(User user, std::vector<Item>& items) {...}

    absl::StatusOr<Item> findItems(int userId) { 
        ASSIGN_OR_RETURN(auto user, loadUserById(userId));
        std::vector<Item> items;
        RETURN_IF_ERROR(populateItems(user, items));
        for (auto& item: items) {
        ...     
        }
   }
ASSIGN_OR_RETURN and RETURN_IF_ERROR essentially preprocessor macroses, that expand, more or less into.

   absl::StatusOr<Item> findItems(int userId) { 
      auto userOrStatus = loadUserById(userId);
      if (!userOrStatus.ok()) return userOrStatus.status();
      auto user = *userOrStatus;
      std::vector<Item> items;
      absl::Status st2 = populateItems(user, items));
      if (!st2.ok()) return st2;
   }
No long and ugly method invocation chains, no weirdly looking code - everything just works. You can see real life example here: https://github.com/protocolbuffers/protobuf/blob/bd7fe97e8c1...

Again, even inside Google there were docs that considered those macroses bad and suggested to write straightforward code, but I'm in the camp who considers them useful and, maybe, sole good use of preprocessor macros that I ever seen, as there are no other way to clearly and concisely express that in majority of languages.

F# has something like that with Computational Expressions, but they are still limited.