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.
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.
Related
I am trying to write some example of a concept that requires implementing a "read" method. This "read" method would take a number of bytes to read and an output iterator to write the data.
So far it looks like this:
template <typename T>
concept DataSourceReader = requires (T reader, std::size_t bytes_to_read) {
{ reader.read(bytes_to_read, std::output_iterator<std::uint8_t>) } -> std::same_as<std::size_t>;
};
However it has multiple issues:
std::output_iterator requires 2 template arguments but I can't figure out what they are
the std::output_iterator argument does not put any constraints (e.g. I can call with any types)
It is also not clear why bytes_to_read need to be in top when { reader.read(5, ...) } works but { reader.read(std::size_t, ...) } does not.
It would seem ok to allow { reader.read(std::same_as<std::size_t>, ...) } since that's how return types can be constrained but it is also wrong.
I need to iterate over folder either recursively or not (given the boolean parameter). I have discovered there is fs::recursive_directory_iterator() and also fs::directory_iterator(). In Java, I would expect them to implement the same interface or share the common ancestor so that I could substitute the needed one. But for some reason the two iterators do not share the common ancestor, forcing the to write the code like:
if (recursive_) {
path = recursive_iterator_->path();
recursive_iterator_++;
} else {
path = plain_iterator_->path();
plain_iterator_++;
}
I cannot believe this is how it is supposed to work. I also initially assumed there are some options to turn off recursion for recursive_directory_iterator but seems no any between std::filesystem::directory_options.
The value is not known at the compile time. I think it should be possible to use something like a closure or even subclass with virtual method but looks a bit like overkill.
Should I simply use conditionals switching between the two iterators as needed, or there are better approaches?
implement the same interface
They do. They are both InputIterators, that dereference to const std::filesystem::directory_entry&.
C++ avoids virtual by default.
You can use boost::any_range to type erase the recursiveness.
template <typename... Args>
auto make_directory_range(bool recursive, Args... args) {
return recursive
? boost::make_iterator_range(fs::recursive_directory_iterator(args...), fs::recursive_directory_iterator()) | boost::adaptors::type_erased()
: boost::make_iterator_range(fs::directory_iterator(args...), fs::directory_iterator());
}
using iterator_t = decltype(make_directory_range(true).begin());
auto range = make_directory_range(recursive_, args...);
iterator_t iterator = range.begin();
iterator_t end = range.end();
The usual way of dealing with a static polymorphism situation like this is to use a helper template:
template<class F,class ...AA>
void for_each_file(F f,bool rec,AA &&...aa) {
const auto g=[&](auto end) {
std::for_each(decltype(end)(std::forward<AA>(aa)...),
end,std::move(f));
};
if(rec) g(fs::recursive_directory_iterator());
else g(fs::directory_iterator());
}
std::size_t count(const fs::path &d,bool rec) {
std::size_t n=0;
for_each_file([&](fs::directory_entry) {++n;},rec,d);
return n;
}
This approach does have limitations: it makes it harder to break out of the “loop”, for example.
Edit: thanks for the prompt replies. I found my notation about T is confusing.
T is not a template argument but actual type hard coded in the switch clause, according to case.
I have the following template function that takes two template arguments, while the latter is optional:
template <typename F, typename RT = int>
pipe(Input input, F operation)
{
// the Input is a container with its value stored in unknown type T
// and I do the following switch to call different version of
// operation (a template function as well) according to T
switch (input.type())
{
case 0: RT output = (RT) operation<char>(input);break;
case 1: RT output = (RT) operation<int>(input);break;
...
}
return output;
}
What I would like to achieve is to allow determine RT according to the parsed data type T when RT is not explicitly set, i.e.:
when call with pipe<F>(input, operation), the return type would be the same as the data type of input;
when call with pipe<F, RT>(input, operation), the return type would be set to RT regardless what input data type is.
I can achieve this for now by doing overloading, which involves copy paste the nearly the same chunk of code with a little modification
Therefore I am asking if there is a way to avoid this by having a special "dummy" type as the default RT:
template <typename F, typename RT = dummy>
and have some switch in the function pipe like this:
if (RT == dummy)
// use T determined from switch as RT
else
// use the specified RT
Any suggestions?
You should reverse your ordering. If you make the "dummy" template 2nd, to provide it explicitly, you will also have to provide the first type - and then you won't be able to take advantage of template deduction.
Instead, provide two overloads of pipe. One in which RT is explicitly provided:
template <typename RT, typename F>
void pipe(Input input, F operation) {
switch (input.type())
{
case 0: RT output = (RT) operation<char>(input);break;
case 1: RT output = (RT) operation<int>(input);break;
...
}
return output;
}
And one in which it's determined from Input and calls the other one:
template <typename F>
void pipe(Input input, F operation)
{
pipe<typename Input::value_type>(input, operation);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
// or whatever metafunction
}
No dummy or need to avoid deduction necessary.
One thing that could actually help you, even if it doesn't answer your question directly is to use the internal typedefs of your container.
All the STL containers have a value_type alias which identify the element which are actually contained within.
Then you can call:
(RT) operation<input::value_type>(input)
I'm really happy to have discovered for_each_arg(...), which makes dealing with argument packs much easier.
template<class F, class...Ts>
F for_each_arg(F f, Ts&&...a) {
return (void)std::initializer_list<int>{(ref(f)((Ts&&)a),0)...}, f;
}
I'm, however, confused on its correct usage. There are many arguments that need to be perfectly forwarded, but am I performing any unnecessary forwarding?
Reading the code becomes harder with excessive fowarding.
struct UselessContainer
{
// Expects a perfectly-forwarded item to emplace
template<typename T> void add(T&&) { }
};
// Creates an `UselessContainer` already filled with `mArgs...`
auto makeUselessContainer(TArgs&&... mArgs)
{
using namespace std;
UselessContainer result;
for_each_arg
(
[&result, &mArgs...] // Am I capturing the `mArgs...` pack correctly here?
(auto&& mX) // Am I passing the arguments to the lambda correctly here?
{
// Is this `forward` necessary?
result.add(forward<decltype(mX)>(mX));
// Could it be replaced with
// `result.add(forward(mX));`
// ?
},
forward<TArgs>(mArgs)... // I assume this `forward` is necessary.
);
return result;
}
All my questions/doubts are expressed in the comments in the above code example.
Every forward in your code is indeed necessary to perfectly forward all arguments until the end. Names of rvalue references are lvalues, so unless you're forwarding everytime you pass arguments on, the value category information is lost.
Also it is impossible to call forward without an explicit template argument list as the template parameter is only used in one, non-deduced context. In fact, a function template called without an explicit argument list cannot do the job.
You can try a macro to somewhat shorten the code:
#define FORWARD(...) std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__)
It then becomes
for_each_arg
(
// Removed superfluous capture
[&result] (auto&& mX) {
result.add(FORWARD(mX));
},
FORWARD(mArgs)...
);
It's also possible to use a macro instead of for_each_arg in the first place:
#define FOR_EACH_ARG(...) (void)std::initializer_list<int>{((__VA_ARGS__),0)...}
FOR_EACH_ARG( result.add(forward<TArgs>(mArgs)) );
for_each_arg (
[&](auto&& mX){
result.add(std::forward<decltype(mX)>(mX));
},
std::forward<TArgs>(mArgs)...
);
Just capture & when making this kind of lambda. If you must list, only &result need be captured.
forward<?> is always used with a type parameter.
Note Eric's for_each_arg is imperfect, and mostly about doing it in 140 characters or less. ;) Its imperfections are mild, and harmless here.
Here is an alternative:
First, write this:
template<class...Fs>
void do_in_order(Fs&&...fs){
int _[]={0,
(((void)(std::forward<Fs>(fs)())),0)...
};
(void)_; // kills warnings
}
it takes zero arg lambdas, and runs them left to right.
Then replace the call to for_each_arg with:
do_in_order(
[&]{
result.add(std::forward<TArgs>(mArgs));
}...
);
the downside is that more compilers won't like the above.
Ordering of the expressions in the do_in_order is guaranteed by [dcl.init] and [dcl.init.list] sections in n4296 8.5.4/4 8.5.4/1 8.5/15 8.5/1. The initialization is a copy-list-initialization (8.5/15 and 8.5.4/1), is a "initializer-list of a braced-init-list" (8.5/1) and as such is sequenced left to right (8.5.4/4).
I have the following C++ snippet:
template_par<std::string> a("name", "Joh Node");
template_par<int> b("age", 23);
std::string result = templater<SomeTemplateClass>().templatize(a, b).get();
Which tries to implement a template engine for various purposes. The important parts of the templater class are:
template<class T>
class templater
{
std::map<std::string, std::string> kps;
public:
template <typename T1>
templater& templatize(template_par<T1> b)
{
kps.insert(make_pair(b.getKey(), to_string(b.getValue())));
return *this;
}
template<typename T1, typename... Args1>
templater& templatize(T1 first, Args1... args)
{
kps.insert(make_pair(first.getKey(), to_string(first.getValue())));
return templatize(args...);
}
}
ie. a template function with variable arguments .... template_par<T> are just template parameter classes for basic stuff. Right now as it is, this works, does the job nicely.
However, I would like to be able to shorten somehow the way I call the templatize method not only for aesthetics but also for a challenge... I think it would look much nicer something like:
std::string result = templater<SomeTemplateClass>().templatize(
"name" -> "Joh Node",
"age" -> 42
);
However this approach is not feasible due to the operator -> being a somewhat rigid piece of C++ ... (std::string result = templater<SomeTemplateClass>().templatize is not the important part here, that can be hided in a friendly construct, I am more worried about the variable number of parameters)
Any good beautification ideas for the challenge above?
Take a look at Boost.Assign, in particular the map assignment part of it, which you could co-opt here:
std::string result = templater<SomeTemplateClass>()
("Name", "Joh Node")
("Age", 42).templatize();
I would avoid getting much more creative than that, it makes the code cryptic. That said, if you want to experiment wildly, you might like my named operators, which would allow syntax such as:
std::string result = templater<SomeTemplateClass>().templatize(
"name" <is> "Joh Node",
"age" <is> 42
);
Here, is can be any valid C++ identifier. So conventional operators are unfortunately out, but pretty much everything flies. Even, if you really want to push it, <_>.