Skip to content

Commit

Permalink
Merge pull request #1225 from h-2/cpo_the_last_one
Browse files Browse the repository at this point in the history
[MISC] various fixes related to alphabet CPOs
  • Loading branch information
h-2 committed Sep 5, 2019
2 parents 34e7b9b + 57af72b commit db42822
Show file tree
Hide file tree
Showing 18 changed files with 493 additions and 400 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ If possible, provide tooling that performs the changes, e.g. a shell-script.

## API changes

* **Customising for third party types has changes slightly:**
You are only affected if you added types to `seqan3::custom::`.
Please see [About Customisation](http://docs.seqan.de/seqan/3-master-user/about_customisation.html).

#### Argument parser

* **Changed class signature of input/output file validators:**
Expand Down
27 changes: 19 additions & 8 deletions doc/about/customisation/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ less visible by having the template parameters deduced in many scenarios.

We strive to document the exact requirements for every template parameter in the API documentation; often these
are also enforced via C++ concepts. Wherever other types meet the given requirements, you can use these
type as arguments – independent of whether they are standard library types, your own types or types from another
types as arguments – independent of whether they are standard library types, your own types or types from another
library. See \ref tutorial_concepts for more details on this.

Some requirements are more elaborate and depend on a specialisation or "overload" of another entity from the
Expand All @@ -25,14 +25,25 @@ One such customisation point is seqan3::to_rank. Instead of the seqan3::semialph
member functions or free functions in your namespace, it always checks for a valid definition of seqan3::to_rank which
in turn resolves to different possible implementations.

To customise one of our customisation points, do one of the following:
To customise one of our customisation points, follow the instructions in its API documentation. Typically, you have the
choice between the following options:

* Provide functionality as members *or* inside the namespace of your type (will be picked up via
[argument dependent lookup](https://en.cppreference.com/w/cpp/language/adl)).
* If you adapt a third party's type and you cannot add to that type's namespace, provide functionality inside the
namespace `seqan3::custom` (this is the "upload namespace").
* **Never** add names (types, functions, variables...) to namespace `seqan3` and never explicitly specialise one
of our templates or overload one of our functions.
1. Provide functionality as members.
2. Provide friends or free functions inside the namespace of your type (will be picked up via
[argument dependent lookup](https://en.cppreference.com/w/cpp/language/adl)).
3. Specialise the respective template in `seqan3::custom::` for that type and provide the needed functionality as
static members of that specialisation (this is an "upload space" for specialisations).

The priority is bottom to top (i.e. solutions to 3. will be preferred over 1. and 2.), but we strongly recommend to
use 1. and 2., because they are much easier to implement. Only use 3. if you adapt a third party's type and you cannot
add to that type's definition and/or namespace.

\warning
**Never** add anything (types, functions, variables...) to namespace `seqan3` and never explicitly specialise one
of our templates (except those in seqan3::custom) or overload one of our functions.

The \link howto_write_an_alphabet HowTo on creating your own alphabet \endlink provides many examples of how to
satisfy the requirements of customisation point objects.

More technical background on this topic can be found here:

Expand Down
10 changes: 10 additions & 0 deletions doc/howto/write_an_alphabet/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,15 @@ without defining the (in)equality and comparison operators. Opposed to the examp
to model the functionality.
\snippet test/unit/alphabet/custom_alphabet_test.cpp my_alph

## Adaptation of a third party type {#howto_write_an_alphabet_custom}

This example is similar to the previous one, but assuming that you cannot add anything to the namespace of
the type that you wish to adapt.
In that case, you need to specialise the seqan3::custom::alphabet class template and provide the required functionality
as static members.

\snippet test/unit/alphabet/custom_alphabet3_test.cpp third_party_type

## Implementation of a non-default-constructible class

This is an example of a custom alphabet that is not default-constructible and that has a non-default overload for
Expand All @@ -174,3 +183,4 @@ from '1', 't' and 'T' for value 1 as well as from '0', 'f' and 'F' for value 0.
\note
You should really make your alphabet types [no-throw-default-constructible](\ref std::is_nothrow_default_constructible)
if you can!

129 changes: 52 additions & 77 deletions include/seqan3/alphabet/adaptation/char.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,103 +27,78 @@

#include <limits>

#include <seqan3/alphabet/concept.hpp>
#include <seqan3/core/concept/core_language.hpp>
#include <seqan3/core/detail/customisation_point.hpp>
#include <seqan3/core/detail/int_types.hpp>
#include <seqan3/std/concepts>

namespace seqan3::detail
{
//!\addtogroup adaptation
//!\{

//!\brief Whether a type is `char`, `char16_t`, `char32_t` or `wchar_t` (type trait).
template <typename type, typename type_no_ref = std::remove_reference_t<type>>
constexpr bool is_char_adaptation_v = std::same_as<type_no_ref, char> ||
std::same_as<type_no_ref, char16_t> ||
std::same_as<type_no_ref, char32_t> ||
std::same_as<type_no_ref, wchar_t>;
//!\}
//!\ingroup adaptation
template <typename type>
constexpr bool is_char_adaptation_v = std::same_as<type, char> ||
std::same_as<type, char16_t> ||
std::same_as<type, char32_t> ||
std::same_as<type, wchar_t>;
} // namespace seqan3::detail

namespace seqan3::custom
{

/*!\name Free function wrappers for the char alphabet adaptation
* \brief For `char`, `char16_t` and `char32_t` do conversion to/from uint types.
* \ingroup adaptation
* \{
*/

/*!\brief Return the number of values the char type can take.
/*!\brief Alphabet specific customisations for builtin char types.
* \tparam char_type One of `char`, `char16_t`, `char32_t` or `wchar_t`.
* \param chr The parameters actual value is ignored.
* \returns The respective sizes (e.g. 256 for `char`).
* \ingroup adaptation
*/
template <typename char_type>
//!\cond
requires seqan3::detail::is_char_adaptation_v<char_type>
requires detail::is_char_adaptation_v<char_type>
//!\endcond
constexpr auto alphabet_size(char_type const & SEQAN3_DOXYGEN_ONLY(chr)) noexcept
struct alphabet<char_type>
{
return detail::min_viable_uint_t<detail::size_in_values_v<char_type>>{detail::size_in_values_v<char_type>};
}
//!\brief The number of values the char type can take (e.g. 256 for `char`).
static constexpr auto alphabet_size =
detail::min_viable_uint_t<detail::size_in_values_v<char_type>>{detail::size_in_values_v<char_type>};

/*!\brief Converting char to char is no-op (it will just return the value you pass in).
* \tparam char_type One of `char`, `char16_t`, `char32_t` or `wchar_t`.
* \param chr The alphabet letter that you wish to convert to char (no-op).
* \returns `chr`.
*/
template <typename char_type>
//!\cond
requires seqan3::detail::is_char_adaptation_v<char_type>
//!\endcond
constexpr char_type to_char(char_type const chr) noexcept
{
return chr;
}
/*!\brief Converting char to char is no-op (it will just return the value you pass in).
* \param[in] chr The alphabet letter that you wish to convert to char (no-op).
* \returns `chr`.
*/
static constexpr char_type to_char(char_type const chr) noexcept
{
return chr;
}

/*!\brief Convert char to rank by casting to an unsigned integral type of same size.
* \tparam char_type One of `char`, `char16_t`, `char32_t` or `wchar_t`.
* \param chr The alphabet letter that you wish to convert to rank.
* \returns The letter's value in the alphabet's rank type (usually a `uint*_t`).
*/
template <typename char_type>
//!\cond
requires seqan3::detail::is_char_adaptation_v<char_type>
//!\endcond
constexpr auto to_rank(char_type const chr) noexcept
{
return static_cast<::seqan3::detail::min_viable_uint_t<alphabet_size(char_type{}) - 1>>(chr);
}
/*!\brief Convert char to rank by casting to an unsigned integral type of same size.
* \param[in] chr The alphabet letter that you wish to convert to rank.
* \returns The letter's value in the alphabet's rank type (usually a `uint*_t`).
*/
static constexpr auto to_rank(char_type const chr) noexcept
{
return static_cast<detail::min_viable_uint_t<alphabet_size - 1>>(chr);
}

/*!\brief Assign a char to the char type (same as calling `=`).
* \tparam char_type One of `char`, `char16_t`, `char32_t` or `wchar_t`.
* \param chr The alphabet letter that you wish to assign to.
* \param chr2 The `char` value you wish to assign.
* \returns A reference to the alphabet letter you passed in.
*/
template <typename char_type>
//!\cond
requires detail::is_char_adaptation_v<char_type>
//!\endcond
constexpr char_type & assign_char_to(char_type const chr2, char_type & chr) noexcept
{
return chr = chr2;
}
/*!\brief Assign a char to the char type (same as calling `=`).
* \param[in] chr2 The `char` value you wish to assign.
* \param[in,out] chr The alphabet letter that you wish to assign to.
* \returns A reference to the alphabet letter you passed in.
*/
static constexpr char_type & assign_char_to(char_type const chr2, char_type & chr) noexcept
{
return chr = chr2;
}

/*!\brief Assigning a rank to a char is the same as assigning it a numeric value.
* \tparam char_type One of `char`, `char16_t`, `char32_t` or `wchar_t`.
* \param chr The alphabet letter that you wish to assign to.
* \param rank The `rank` value you wish to assign.
* \returns A reference to the alphabet letter you passed in.
*/
template <typename char_type>
//!\cond
requires detail::is_char_adaptation_v<char_type>
//!\endcond
constexpr char_type & assign_rank_to(decltype(to_rank(char_type{})) const rank, char_type & chr) noexcept
{
return chr = rank;
}
//!\}
/*!\brief Assigning a rank to a char is the same as assigning it a numeric value.
* \param[in] rank The `rank` value you wish to assign.
* \param[in,out] chr The alphabet letter that you wish to assign to.
* \returns A reference to the alphabet letter you passed in.
*/
static constexpr char_type & assign_rank_to(decltype(alphabet::to_rank(char_type{})) const rank,
char_type & chr) noexcept
{
return chr = rank;
}
};

} // namespace seqan3::custom
135 changes: 54 additions & 81 deletions include/seqan3/alphabet/adaptation/uint.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,107 +27,80 @@

#include <limits>

#include <seqan3/alphabet/concept.hpp>
#include <seqan3/core/detail/customisation_point.hpp>
#include <seqan3/core/detail/int_types.hpp>
#include <seqan3/std/concepts>

namespace seqan3::detail
{
//!\addtogroup adaptation
//!\{

//!\brief Whether a type is `uint8_t`, `uint16_t` or `uint32_t`.
template <typename type, typename type2 = std::remove_reference_t<type>>
constexpr bool is_uint_adaptation_v = std::same_as<type2, uint8_t> ||
std::same_as<type2, uint16_t> ||
std::same_as<type2, uint32_t>;
//!\}
//!\ingroup adaptation
template <typename type>
constexpr bool is_uint_adaptation_v = std::same_as<type, uint8_t> ||
std::same_as<type, uint16_t> ||
std::same_as<type, uint32_t>;
} // namespace seqan3::detail

namespace seqan3::custom
{

/*!\name Free function wrappers for the uint alphabet adaptation
* \brief For `uint8_t`, `uint16_t` and `uint32_t` do conversion to/from char types.
/*!\brief Alphabet specific customisations for unsigned integral types.
* \tparam uint_type Any of `uint8_t`, `uint16_t` and `uint32_t`.
* \ingroup adaptation
* \{
*/

/*!\brief Return the number of values the uint type can take.
* \tparam uint_type One of `uint8_t`, `uint16_t` or `uint32_t`.
* \param intgr The parameter's actual value is ignored.
* \returns The respective size (e.g. 256 for `uint8_t`).
*/
template <typename uint_type>
//!\cond
requires ::seqan3::detail::is_uint_adaptation_v<uint_type>
requires seqan3::detail::is_uint_adaptation_v<uint_type>
//!\endcond
constexpr auto alphabet_size(uint_type const & SEQAN3_DOXYGEN_ONLY(intgr)) noexcept
struct alphabet<uint_type>
{
return detail::min_viable_uint_t<detail::size_in_values_v<uint_type>>{detail::size_in_values_v<uint_type>};
}
//!\brief Return the number of values the uint type can take (e.g. 256 for `uint8_t`).
static constexpr auto alphabet_size =
detail::min_viable_uint_t<detail::size_in_values_v<uint_type>>{detail::size_in_values_v<uint_type>};

/*!\brief Converting uint to char casts to a character type of same size.
* \tparam uint_type One of `uint8_t`, `uint16_t` or `uint32_t`.
* \param intgr The alphabet letter that you wish to convert to char.
* \returns The letter's value in the alphabet's rank type (usually `uint`).
*/
template <typename uint_type>
//!\cond
requires detail::is_uint_adaptation_v<uint_type>
//!\endcond
constexpr auto to_char(uint_type const intgr) noexcept
{
if constexpr (std::same_as<uint_type, uint8_t>)
return static_cast<char>(intgr);
else if constexpr (std::same_as<uint_type, uint16_t>)
return static_cast<char16_t>(intgr);
else
return static_cast<char32_t>(intgr);
}
/*!\brief Converting uint to char casts to a character type of same size.
* \param[in] intgr The alphabet letter that you wish to convert to char.
* \returns The letter's value in the alphabet's rank type (usually `uint`).
*/
static constexpr auto to_char(uint_type const intgr) noexcept
{
if constexpr (std::same_as<uint_type, uint8_t>)
return static_cast<char>(intgr);
else if constexpr (std::same_as<uint_type, uint16_t>)
return static_cast<char16_t>(intgr);
else
return static_cast<char32_t>(intgr);
}

/*!\brief Converting uint to rank is a no-op (it will just return the value you pass in).
* \tparam uint_type One of `uint8_t`, `uint16_t` or `uint32_t`.
* \param intgr The alphabet letter that you wish to convert to rank.
* \returns `intgr`.
*/
template <typename uint_type>
//!\cond
requires detail::is_uint_adaptation_v<uint_type>
//!\endcond
constexpr uint_type to_rank(uint_type const intgr) noexcept
{
return intgr;
}
/*!\brief Converting uint to rank is a no-op (it will just return the value you pass in).
* \param[in] intgr The alphabet letter that you wish to convert to rank.
* \returns `intgr`.
*/
static constexpr uint_type to_rank(uint_type const intgr) noexcept
{
return intgr;
}

/*!\brief Assign from a character type via implicit or explicit cast.
* \tparam uint_type One of `uint8_t`, `uint16_t` or `uint32_t`.
* \param intgr The alphabet letter that you wish to assign to.
* \param chr The `char` value you wish to assign.
* \returns A reference to the alphabet letter you passed in.
*/
template <typename uint_type>
//!\cond
requires detail::is_uint_adaptation_v<uint_type>
//!\endcond
constexpr uint_type & assign_char_to(decltype(to_char(uint_type{})) const chr, uint_type & intgr) noexcept
{
return intgr = chr;
}
/*!\brief Assign from a character type via implicit or explicit cast.
* \param[in] chr The `char` value you wish to assign.
* \param[in,out] intgr The alphabet letter that you wish to assign to.
* \returns A reference to the alphabet letter you passed in.
*/
static constexpr uint_type & assign_char_to(decltype(to_char(uint_type{})) const chr, uint_type & intgr) noexcept
{
return intgr = chr;
}

/*!\brief Assign a rank to to the uint (same as calling `=`).
* \tparam uint_type One of `uint8_t`, `uint16_t` or `uint32_t`.
* \param intgr The alphabet letter that you wish to assign to.
* \param intgr2 The `rank` value you wish to assign.
* \returns A reference to the alphabet letter you passed in.
*/
template <typename uint_type>
//!\cond
requires detail::is_uint_adaptation_v<uint_type>
//!\endcond
constexpr uint_type & assign_rank_to(uint_type const intgr2, uint_type & intgr) noexcept
{
return intgr = intgr2;
}
/*!\brief Assign a rank to to the uint (same as calling `=`).
* \param[in] intgr2 The `rank` value you wish to assign.
* \param[in,out] intgr The alphabet letter that you wish to assign to.
* \returns A reference to the alphabet letter you passed in.
*/
static constexpr uint_type & assign_rank_to(uint_type const intgr2, uint_type & intgr) noexcept
{
return intgr = intgr2;
}
};

//!\}
} // namespace seqan3::custom
1 change: 1 addition & 0 deletions include/seqan3/alphabet/cigar/cigar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#pragma once

#include <seqan3/alphabet/adaptation/uint.hpp>
#include <seqan3/alphabet/cigar/cigar_op.hpp>
#include <seqan3/alphabet/composite/alphabet_tuple_base.hpp>
#include <seqan3/core/detail/debug_stream_type.hpp>
Expand Down
Loading

0 comments on commit db42822

Please sign in to comment.