2

I have a function that does a base 64 encode using Boost. It takes two template parameters: One for the type of container used for input, and one for the type of container used for output. This allows you to do things like provide binary data using a std::vector but get a std::string back.

Here's the code:

template<typename OutputContainer, typename InputContainer>
OutputContainer Encode(InputContainer const& data)
{
    using namespace boost::archive::iterators;
    using base64_it = base64_from_binary<transform_width<
        typename InputContainer::const_iterator, 6, 8>>;

    OutputContainer result(base64_it(data.begin()), base64_it(data.end()));

    static std::string const padding[] { "", "==", "="};
    auto const& pad = padding[data.size() % 3];
    result.insert(result.end(), pad.begin(), pad.end());

    return result;
}

Usage example:

std::string data = "Hello World!+";
auto encoded = Encode<std::string>(data);

Live sample here

Note in the example above, I still had to provide the template argument for the output container even though it's the same type as the input container. In these scenarios, I'd like the OutputContainer template parameter to be optional and instead use the type deduced by InputContainer. I am not sure what workarounds or adjustments I can perform to get this kind of interface, but it would be nice to see what SO community can come up with.

Note I am also open to a more iterator-centric approach, but I avoided it due to redundancy/boilerplate (I think it's simpler to pass in containers than iterators). If it ends up looking like the 4-argument version of std::transform() I think I'll be a lot less satisfied with the solution.

void.pointer
  • 24,859
  • 31
  • 132
  • 243

1 Answers1

4

Using an overload (not a specialization) for the case where InputContainer and OutputContainer is the same, enable_if to disable the original implementation in this case, it's possible to achieve what you're asking for. A caveat is that it will no longer be possible to explicitly specify both container types if they are the same, unless a third argument is also provided:

template<typename OutputContainer, typename InputContainer, typename = std::enable_if_t<!std::is_same_v<OuptputContainer, InputContainer>>>
OutputContainer Encode(InputContainer const& data)
{
    // Your implementation
}

template<typename OutputContainer>
OutputContainer Encode(OutputContainer const& data)
{
    return Encode<OutputContainer, OutputContainer, void>(data);
}

Example on Godbolt.

You
  • 22,800
  • 3
  • 51
  • 64