Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

motivation/plug_error_code.html refers to std, not to boost #183

Closed
Warchant opened this issue Apr 13, 2019 · 9 comments

Comments

@Warchant
Copy link

commented Apr 13, 2019

UPD: Okay, I found <boost/outcome.hpp>.

Now, another question - previously in my code I registered enums as error codes, same as in this example https://ned14.github.io/outcome/motivation/plug_error_code/

I plugged in boost edition, did the following:

namespace outcome = BOOST_OUTCOME_V2_NAMESPACE;
#define OUTCOME_TRY(...) BOOST_OUTCOME_TRY(__VA_ARGS__)

And now my code does not compile, because enums can not be registered as error codes (compiler says no viable conversion from <my enum> to boost::outcome::result<...>

@Warchant Warchant changed the title Boost 1.70 is out. How to include outcome? Boost 1.70 is out. How to use outcome? Apr 13, 2019

@ned14

This comment has been minimized.

Copy link
Owner

commented Apr 13, 2019

Can you a self-contained repro please?

@Warchant

This comment has been minimized.

Copy link
Author

commented Apr 13, 2019

@ned14

This is how we use outcome right now: https://github.com/Warchant/boost.outcome-motivational-example/tree/standalone (branch standalone). It compiles and works as expected.

Note that we added some macro magic to reduce boilerplate related to making enum work as error code (file include/outcome/outcome-register.hpp)

This is what I do to update to boost.outcome:

Warchant/boost.outcome-motivational-example@standalone...boost (branch boost)

This is what I get:

/Users/bogdan/tools/boost.outcome-motivational-example/src/main.cpp: In function 'boost::outcome_v2::result<int> lib::convert(std::__cxx11::string)':
/Users/bogdan/tools/boost.outcome-motivational-example/src/main.cpp:12:28: error: could not convert 'EmptyString' from 'tezt::ConversionErrc' to 'boost::outcome_v2::result<int>' {aka 'boost::outcome_v2::basic_result<int, boost::system::error_code, boost::outcome_v2::policy::error_code_throw_as_system_error<int, boost::system::error_code, void> >'}
     return ConversionErrc::EmptyString;
                            ^~~~~~~~~~~
/Users/bogdan/tools/boost.outcome-motivational-example/src/main.cpp:15:28: error: could not convert 'IllegalChar' from 'tezt::ConversionErrc' to 'boost::outcome_v2::result<int>' {aka 'boost::outcome_v2::basic_result<int, boost::system::error_code, boost::outcome_v2::policy::error_code_throw_as_system_error<int, boost::system::error_code, void> >'}
     return ConversionErrc::IllegalChar;
                            ^~~~~~~~~~~
/Users/bogdan/tools/boost.outcome-motivational-example/src/main.cpp:18:28: error: could not convert 'TooLong' from 'tezt::ConversionErrc' to 'boost::outcome_v2::result<int>' {aka 'boost::outcome_v2::basic_result<int, boost::system::error_code, boost::outcome_v2::policy::error_code_throw_as_system_error<int, boost::system::error_code, void> >'}
     return ConversionErrc::TooLong;

As far as I understand, compiler can not implicitly cast std::error_code to boost::system::error_code, therefore it breaks...

@ned14

This comment has been minimized.

Copy link
Owner

commented Apr 13, 2019

Are you aware that including boost/outcome.hpp includes boost/outcome/boost_result.hpp, whereas including standalone outcome.hpp includes outcome/std_result.hpp? In other words, outcome::result<> aliases a boost implementation in Boost, and a std implementation in standalone.

@Warchant

This comment has been minimized.

Copy link
Author

commented Apr 14, 2019

Yes. Does it matter?
Implementations should be interoperable, isn't it?

@ned14

This comment has been minimized.

Copy link
Owner

commented Apr 14, 2019

I mentioned it because from the error messages you posted, it would seem that you have not declared to Boost System how to convert your enum codes into boost::system::error_code.

@Warchant

This comment has been minimized.

Copy link
Author

commented Apr 14, 2019

I believe this guide should be changed to contain boost equivalents: https://boostorg.github.io/outcome/motivation/plug_error_code.html

Otherwise, it does not compile with boost outcome.

#include <iostream>
#include <string>        // for string printing
-#include <system_error>  // bring in std::error_code et al
+#include <boost/system/error_code.hpp>

// This is the custom error code enum
enum class ConversionErrc
{
  Success     = 0, // 0 should not represent an error
  EmptyString = 1,
  IllegalChar = 2,
  TooLong     = 3,
};

-namespace std
-{
  // Tell the C++ 11 STL metaprogramming that enum ConversionErrc
  // is registered with the standard error code system
-  template <> struct is_error_code_enum<ConversionErrc> : true_type
+  template <> struct boost::system::is_error_code_enum<ConversionErrc> : true_type
  {
  };
-}

namespace detail
{
-  // Define a custom error code category derived from std::error_category
+ // Define a custom error code category derived from boost::system::error_category
-  class ConversionErrc_category : public std::error_category
+  class ConversionErrc_category : public boost::system::error_category
  {
  public:
    // Return a short descriptive name for the category
    virtual const char *name() const noexcept override final { return "ConversionError"; }
    // Return what each enum means in text
    virtual std::string message(int c) const override final
    {
      switch (static_cast<ConversionErrc>(c))
      {
      case ConversionErrc::Success:
        return "conversion successful";
      case ConversionErrc::EmptyString:
        return "converting empty string";
      case ConversionErrc::IllegalChar:
        return "got non-digit char when converting to a number";
      case ConversionErrc::TooLong:
        return "the number would not fit into memory";
      default:
        return "unknown";
      }
    }
    // OPTIONAL: Allow generic error conditions to be compared to me
    virtual std::error_condition default_error_condition(int c) const noexcept override final
    {
      switch (static_cast<ConversionErrc>(c))
      {
      case ConversionErrc::EmptyString:
 -       return make_error_condition(boost::system::errc::invalid_argument);
+        return make_error_condition(boost::system::errc::invalid_argument);
      case ConversionErrc::IllegalChar:
-        return make_error_condition(boost::system::errc::invalid_argument);
+        return make_error_condition(boost::system::errc::invalid_argument);
      case ConversionErrc::TooLong:
-        return make_error_condition(boost::system::errc::result_out_of_range);
+        return make_error_condition(boost::system::errc::result_out_of_range);
      default:
        // I have no mapping for this code
  -      return std::error_condition(c, *this);
 +       return boost::system::error_condition(c, *this);
      }
    }
  };
}

// Define the linkage for this function to be used by external code.
// This would be the usual __declspec(dllexport) or __declspec(dllimport)
// if we were in a Windows DLL etc. But for this example use a global
// instance but with inline linkage so multiple definitions do not collide.
#define THIS_MODULE_API_DECL extern inline

// Declare a global function returning a static instance of the custom category
THIS_MODULE_API_DECL const detail::ConversionErrc_category &ConversionErrc_category()
{
  static detail::ConversionErrc_category c;
  return c;
}


// Overload the global make_error_code() free function with our
// custom enum. It will be found via ADL by the compiler if needed.
-inline std::error_code make_error_code(ConversionErrc e)
+inline boost::system::error_code make_error_code(ConversionErrc e)
{
  return {static_cast<int>(e), ConversionErrc_category()};
}

int main(void)
{
  // Note that we can now supply ConversionErrc directly to error_code
-  std::error_code ec = ConversionErrc::IllegalChar;
+  boost::system::error_code ec = ConversionErrc::IllegalChar;

-  std::cout << "ConversionErrc::IllegalChar is printed by std::error_code as "
+  std::cout << "ConversionErrc::IllegalChar is printed by boost::system::error_code as "
    << ec << " with explanatory message " << ec.message() << std::endl;

  // We can compare ConversionErrc containing error codes to generic conditions
-  std::cout << "ec is equivalent to std::errc::invalid_argument = "
-    << (ec == std::errc::invalid_argument) << std::endl;
-  std::cout << "ec is equivalent to std::errc::result_out_of_range = "
-    << (ec == std::errc::result_out_of_range) << std::endl;
+  std::cout << "ec is equivalent to boost::system::errc::invalid_argument = "
+    << (ec == boost::system::errc::invalid_argument) << std::endl;
+  std::cout << "ec is equivalent to boost::system::errc::result_out_of_range = "
+    << (ec == boost::system::errc::result_out_of_range) << std::endl;
  return 0;
}
@ned14

This comment has been minimized.

Copy link
Owner

commented Apr 15, 2019

Both Boost.Outcome and standalone Outcome can be used with either Boost or std error coding. It's just that the default is different between the two for result and outcome only.

Is it not obvious that the tutorial section applies to both, but is written for the std types? Do we need to spell out to people that the substitution is possible? Do we need a duplicate tutorial section, for boost types only?

@ned14 ned14 added the documentation label Apr 15, 2019

@ned14 ned14 changed the title Boost 1.70 is out. How to use outcome? motivation/plug_error_code.html refers to std, not to boost Apr 15, 2019

@Warchant

This comment has been minimized.

Copy link
Author

commented Apr 15, 2019

@ned14 from my experiments I can see, that:

  • when you use standalone version AND register your enum for std::error_code (std::is_error_code_enum, std::error_code make_error_code), then it works as expected.
  • when you use boost version AND register your enum for std::error_code, it results in a compilation error - compiler needs to have a function boost::system::error_code make_error_code(Enum e), but has std::error_code make_error_code(Enum e); (also you can not have both functions in one TU simultaneously).
  • when you use boost version AND register your enum for boost::system::error_code, it works as expected.

So, I conclude, that in order to use Boost distribution, users need to register enum for boost::system::error_code, which makes that guide irrelevant, since it describes registration for std::error_code

@ned14 ned14 added this to the Boost 1.71 cutoff milestone Jun 17, 2019

ned14 added a commit that referenced this issue Jun 21, 2019
@ned14

This comment has been minimized.

Copy link
Owner

commented Jun 22, 2019

Fixed. Thanks for the feedback!

@ned14 ned14 closed this Jun 22, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.