On 21.03.24 14:43, René Ferdinand Rivera Morell via Boost wrote:
On Thu, Mar 21, 2024 at 3:35 AM Rainer Deyke via Boost <boost@lists.boost.org> wrote:
This is not so good, because it mixes the options of different encodings, resulting in potentially nonsensical combinations:
result = baseX_encode<64>(source, encoding_options::no_padding); result2 = baseX_encode<16>(source, encoding_options::lower_case);
// The lower_case option is non-sensical for base 64; can this // error be caught at compile time? // result3 = baseX_encode<64>(source, encoding_options::lower_case);
It can be caught at compile time. But not with that interface.
Actually it can be caught at compile time even with that interface, if encoding_options is a namespace instead of an enum type and encoding_options::lower_case has a distinct type from encoding_options::no_padding. For example: template<uint8_t> class encoding_option {}; constexpr encoding_option<0x01> lower_case; constexpr encoding_option<0x02> no_padding; template<uint8_t a, uint8_t b> constexpr encoding_option<a | b> operator|( encoding_option<a>, encoding_option<b>) { return {}; } However, I don't see any benefit whatsoever in having a single encoding-neutral template as the main entry point. Specifying 64 as an integer is especially problematic because base-X as a generalization does not exist. Rather, base-64 and base-16 are distinct members of the larger family of bindary-to-text encodings that also includes uuencoding, BinHex, and percent encoding.
Yeah, as Adrey mentions, making this a runtime choice is sufficiently rare that it's not worth even thinking about it. I certainly never had a use for a runtime choice for that. As for a sane interface.. I would think having encoder/decoder templates (perhaps as functors) is the way to go. For example:
auto base64enc = boost::thing::base_encoder<64, boost::thing::encoding_options::no_padding>(); auto encoded = base64enc.encode(data); auto decoded = base64enc.decode(encoded);
This makes it possible to pass the encoder object to generic code without worrying about calling some specific base64 or base16 functions. Having the template args also makes it possible to check valid combinations. It also makes it easier to specialize performant combinations and still cover everything else with not-so-performant default implementation.
Having an encoder object makes sense. Defining a reusable concept for this encoder object makes sense. Defining a single class template for the encoder object does not make sense, because the set of encoder objects I might want to use is open. -- Rainer Deyke (rainerd@eldwood.com)