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
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
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)