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.
Related
Consider the following template recursion function. Its purpose is to create a vector containing all values in the enum class EnumClassName for which its associated predicate function bool xxx<N>() returns true.
template <EnumClassName N = EnumClassName::LAST_ENUM_VALUE>
std::vector<EnumClassName> xxx_stuff()
{
std::vector<EnumClassName> v = xxx_stuff<EnumClassName(static_cast<int>(N) - 1)>();
if (xxx<N>()) {
v.emplace_back(N);
}
return v;
}
with the recursion base case:
template <> std::vector<EnumClassName> xxx_stuff<EnumClassName::FIRST_ENUM_VALUE>()
{
return {};
}
Now assume there are 10 of these functions where the only thing that differs is the xxx name. I.e. we have the functions alpha_stuff with a corresponding predicate function alpha<N>, beta_stuff with a corresponding predicate function beta<N>, etc etc.
Is there any way to not duplicate the above function 10 times, where the only difference is the replacement of xxx with alpha, beta etc in each duplicate?
I am not able to just loop over the enum values, because the xxx<N> functions in turn call another function yyy<N> which will not accept a runtime-generated template argument <N> (forgive my lingo there, I actually have no idea what I'm talking about or how this works, I just know it gave me compilation errors when I tried it that way).
You cannot pass function template as argument or overloaded function, but you can wrap the call in a type, and then
template <typename EnumClassName,
EnumClassName N = EnumClassName::LAST_ENUM_VALUE,
typename F>
std::vector<EnumClassName> xxx_stuff(F&& f)
{
std::vector<EnumClassName> v =
xxx_stuff<EnumClassName, EnumClassName(static_cast<int>(N) - 1)>(f);
if (f(std::integral_constant<EnumClassName, N>{})) {
v.emplace_back(N);
}
return v;
}
with usage
auto v = xxx_stuff<E>([](auto n) { return xxx<n()>(); });
Demo
Note: That recursion can be avoided using std::integer_sequence.
In my work's codebase, I see the following
class custom {
auto set_data_type(custom_type_t type_t) -> custom & {
// set some stuff
// return *this;
}
}
Why can't we simply just do
class custom {
custom & set_data_type(custom_type_t type_t) {
// set some stuff
// return *this;
}
}
What is the point of using auto in this case when you already know the return type and already wrote it out in the ->... place?
It seems auto would only be beneficial if it is used with decltype(arg) and where arg may have varying return types?
I would say style.
Moreover, it allows to be consistent in any contexts,
simple one (as this one),
more useful ones ("complex" decltype(arg) , scoping (-> iterator instead of typename C::iterator)
or required one (lambda).
To me the most use of this feature is when you're using nested type when defining a function body in a cpp file:
class MyLongClassName
{
using ANestedType = ...;
ANestedType myFunction();
}
When you implement to function body, this syntax avoid some repetition:
MyLongClassName::ANestedType MyLongClassName::myFunction()
{ ... }
versus
auto MyLongClassName::myFunction() -> ANestedType
{ ... }
I believe, "auto" should not be used at all. IMHO, this is ugly sibling of void*; it hides types from programmer, while it is better to know exactly what type is used; this makes one's programming style clumsy, confuses and invites bugs. While the only "reasonable" use of this is typename "shortening", it is actually ridiculous, because short type names don't need to replaced, and for long names there is another keyword. IMHO.
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.
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 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, <_>.