Skip to content

Commit

Permalink
Merge pull request #68 from smehringer/cpo
Browse files Browse the repository at this point in the history
[MISC] Remove dependency of seqan3::detail::customisation_point_object
  • Loading branch information
eseiler authored Mar 13, 2022
2 parents b86e79e + 7da43d2 commit da9ee6d
Show file tree
Hide file tree
Showing 8 changed files with 462 additions and 375 deletions.
185 changes: 1 addition & 184 deletions include/sharg/auxiliary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,179 +12,14 @@

#pragma once

#include <unordered_map>
#include <string>
#include <vector>
#include <iostream>

#include <seqan3/core/detail/customisation_point.hpp>

#include <sharg/platform.hpp>

namespace sharg::custom
{

/*!\brief A type that can be specialised to provide customisation point implementations for the sharg::argument_parser
* such that third party types may be adapted.
* \tparam t The type you wish to specialise for.
* \ingroup argument_parser
*
* \details
*
* ### Named Enumerations
*
* In order to use a third party type within the sharg::argument_parser::add_option or
* sharg::argument_parser::add_positional_option call, you can specialise this struct in the following way:
*
* \include test/snippet/argument_parser/custom_argument_parsing_enumeration.cpp
*
* Please note that by default the `t const`, `t &` and `t const &` specialisations of this class inherit the
* specialisation for `t` so you usually only need to provide a specialisation for `t`.
*
* \note Only use this if you cannot provide respective functions in your namespace. See the tutorial
* \ref tutorial_argument_parser for an example of customising a type within your own namespace.
*
* \remark For a complete overview, take a look at \ref argument_parser
*/
template <typename t>
struct argument_parsing
{}; // forward

//!\cond
template <typename t>
struct argument_parsing<t const> : argument_parsing<t>
{};

template <typename t>
struct argument_parsing<t &> : argument_parsing<t>
{};

template <typename t>
struct argument_parsing<t const &> : argument_parsing<t>
{};
//!\endcond

} // sharg::custom

namespace sharg::detail::adl_only
{

//!\brief Poison-pill overload to prevent non-ADL forms of unqualified lookup.
template <typename t>
std::unordered_map<std::string_view, t> enumeration_names(t) = delete;

//!\brief seqan3::detail::customisation_point_object (CPO) definition for sharg::enumeration_names.
//!\ingroup argument_parser
//!\remark For a complete overview, take a look at \ref argument_parser
template <typename option_t>
struct enumeration_names_cpo : public seqan3::detail::customisation_point_object<enumeration_names_cpo<option_t>, 1>
{
//!\brief CRTP base class seqan3::detail::customisation_point_object.
using base_t = seqan3::detail::customisation_point_object<enumeration_names_cpo<option_t>, 1>;
//!\brief Only this class is allowed to import the constructors from #base_t. (CRTP safety idiom)
using base_t::base_t;

/*!\brief If `option_t` isn't std::is_nothrow_default_constructible, enumeration_names will be called with
* std::type_identity instead of a default constructed alphabet.
*/
template <typename option_type>
using option_or_type_identity
= std::conditional_t<std::is_nothrow_default_constructible_v<std::remove_cvref_t<option_type>>,
std::remove_cvref_t<option_type>,
std::type_identity<option_type>>;

/*!\brief CPO overload (check 1 out of 2): explicit customisation via `sharg::custom::argument_parsing`
* \tparam option_type The type of the option. (Needed to defer instantiation for incomplete types.)
*/
template <typename option_type = option_t>
static constexpr auto SEQAN3_CPO_OVERLOAD(seqan3::detail::priority_tag<1>)
(
/*return*/ sharg::custom::argument_parsing<option_type>::enumeration_names /*;*/
);

/*!\brief CPO overload (check 1 out of 2): argument dependent lookup (ADL), i.e.
* `enumeration_names(option_type{})`
* \tparam option_type The type of the option. (Needed to defer instantiation for incomplete types.)
*
* \details
*
* If the option_type isn't std::is_nothrow_default_constructible,
* `enumeration_names(std::type_identity<option_t>{})` will be called.
*/
template <typename option_type = option_t>
static constexpr auto SEQAN3_CPO_OVERLOAD(seqan3::detail::priority_tag<0>)
(
/*return*/ enumeration_names(option_or_type_identity<option_type>{}) /*;*/
);
};

} // namespace sharg::detail::adl_only

namespace sharg
{

/*!\name Customisation Points
* \{
*/

/*!\brief Return a conversion map from std::string_view to option_type.
* \tparam your_type Type of the value to retrieve the conversion map for.
* \param value The value is not used, just its type.
* \returns A std::unordered_map<std::string_view, your_type> that maps a string identifier to a value of your_type.
* \ingroup argument_parser
* \details
*
* This is a function object. Invoke it with the parameter(s) specified above.
*
* It acts as a wrapper and looks for two possible implementations (in this order):
*
* 1. A static member `enumeration_names` in `sharg::custom::argument_parsing<your_type>` that is of type
* `std::unordered_map<std::string_view, your_type>>`.
* 2. A free function `enumeration_names(your_type const a)` in the namespace of your type (or as `friend`) which
* returns a `std::unordered_map<std::string_view, your_type>>`.
*
* ### Example
*
* If you are working on a type in your own namespace, you should implement a free function like this:
*
* \include test/snippet/argument_parser/custom_enumeration.cpp
*
* **Only if you cannot access the namespace of your type to customize** you may specialize
* the sharg::custom::argument_parsing struct like this:
*
* \include test/snippet/argument_parser/custom_argument_parsing_enumeration.cpp
*
* \remark For a complete overview, take a look at \ref argument_parser
*
* ### Customisation point
*
* This is a customisation point (see \ref about_customisation). To specify the behaviour for your own type,
* simply provide one of the two functions specified above.
*/
template <typename option_type>
//!\cond
requires requires { { detail::adl_only::enumeration_names_cpo<option_type>{}() }; }
//!\endcond
inline auto const enumeration_names = detail::adl_only::enumeration_names_cpo<option_type>{}();
//!\}

/*!\concept sharg::named_enumeration
* \brief Checks whether the free function sharg::enumeration_names can be called on the type.
* \ingroup argument_parser
* \tparam option_type The type to check.
*
* ### Requirements
*
* * A instance of sharg::enumeration_names<option_type> must exist and be of type
* `std::unordered_map<std::string, option_type>`.
*
* \remark For a complete overview, take a look at \ref argument_parser
*/
template <typename option_type>
concept named_enumeration = requires
{
{ sharg::enumeration_names<option_type> };
};

/*!\brief Used to further specify argument_parser options/flags.
* \ingroup argument_parser
*
Expand Down Expand Up @@ -289,21 +124,3 @@ struct argument_parser_meta_data // holds all meta information
};

} // namespace sharg

//!\cond
namespace std
{
template <typename option_type>
requires sharg::named_enumeration<std::remove_cvref_t<option_type>>
inline ostream & operator<<(ostream & s, option_type && op)
{
for (auto & [key, value] : sharg::enumeration_names<option_type>)
{
if (op == value)
return s << key;
}

return s << "<UNKNOWN_VALUE>";
}
} // namespace std
//!\endcond
2 changes: 1 addition & 1 deletion include/sharg/concept.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

#include <seqan3/std/concepts>

#include <sharg/auxiliary.hpp>
#include <sharg/enumeration_names.hpp>

namespace sharg
{
Expand Down
Loading

1 comment on commit da9ee6d

@vercel
Copy link

@vercel vercel bot commented on da9ee6d Mar 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.