recursive variadic template to print out the contents of a parameter pack - recursion

How is it possible to create a recursive variadic template to print out the contents of a paramater pack?
I am trying with this, but it fails to compile:
template <typename First, typename ...Args>
std::string type_name () {
return std::string(typeid(First).name()) + " " + type_name<Args...>();
}
std::string type_name () {
return "";
}
How shall I end the recursion?

There's actually a very elegant way to end the recursion:
template <typename Last>
std::string type_name () {
return std::string(typeid(Last).name());
}
template <typename First, typename Second, typename ...Rest>
std::string type_name () {
return std::string(typeid(First).name()) + " " + type_name<Second, Rest...>();
}
I initially tried template <typename Last> and template <typename First, typename ...Rest> but that was considered ambiguous (Rest can be zero elements). This question then showed me the definitive solution: Compilation Error on Recursive Variadic Template Function
Note, to avoid a bit of code duplication, you could also do:
template <typename Last>
std::string type_name () {
return std::string(typeid(Last).name());
}
template <typename First, typename Second, typename ...Rest>
std::string type_name () {
return type_name<First>() + " " + type_name<Second, Rest...>();
}

You need to use partial specialisation to end the recursion, but since you can't partially specialise free functions in C++, you need to create an implementation class with a static member function.
template <typename... Args>
struct Impl;
template <typename First, typename... Args>
struct Impl<First, Args...>
{
static std::string name()
{
return std::string(typeid(First).name()) + " " + Impl<Args...>::name();
}
};
template <>
struct Impl<>
{
static std::string name()
{
return "";
}
};
template <typename... Args>
std::string type_name()
{
return Impl<Args...>::name();
}
int main()
{
std::cout << type_name<int, bool, char, double>() << std::endl; // "i b c d"
return 0;
}
That first declaration of Impl is just a workaround for a shortcoming in g++ 4.6 (and below). It won't be necessary once it implements variadic templates correctly.
Check it out in action at ideone.com

C++17's if constexpr allows you to do this in one template declaration which is, unlike a lot of the older solutions, pretty easy to understand:
template <typename T, typename ...Args>
std::string type_name() {
if constexpr (!sizeof...(Args)) {
return std::string(typeid(T).name());
} else {
return std::string(typeid(T).name()) + " " + type_name<Args...>();
}
}

As an alternative to non-existing partial specialization for functions, you can use overloading on a typifier class:
#include <string>
#include <iostream>
#include <typeinfo>
template <unsigned int N> struct NumberToType { };
template <typename T>
std::string my_type_name(NumberToType<0> = NumberToType<0>())
{
return std::string(typeid(T).name());
}
template <typename T, typename ...Args>
std::string my_type_name(NumberToType<sizeof...(Args)> = NumberToType<sizeof...(Args)>())
{
return std::string(typeid(T).name()) + " " + my_type_name<Args...>(NumberToType<sizeof...(Args)-1>());
}
int main()
{
std::cout << my_type_name<int, double, char>() << std::endl;
}

As an alternative, you can unpack the parameter pack in-place as in the following example:
#include<string>
#include<iostream>
#include<typeinfo>
template <typename T, typename ...Args>
std::string type_name () {
std::string str = typeid(T).name();
int arr[] = { 0, (str += std::string{" "} + typeid(Args).name(), 0)... };
(void)arr;
return str;
}
int main() {
auto str = type_name<int, double, char>();
std::cout << str << std::endl;
}
Recursion is not required actually to do that.

Use C++17 fold expression:
template <typename ...Args>
std::string type_name () {
return (std::string(typeid(Args).name()) + " " + ...);
}

Related

C++ - Why type casting in Duck Typing

I tried to understand duck typing which is achieved by using templates and SFINAE.
For clear detail please see http://p-nand-q.com/programming/cplusplus/duck_typing_and_templates.html
template <typename T> class repr_type
{
public:
repr_type(const T& o)
:
m_o(o)
{
}
std::string as_string() const
{
return call_as_string<T>(nullptr);
}
private:
template <class C> std::string call_as_string(decltype(&C::as_string)) const
{
return m_o.as_string();
}
template <class C> std::string call_as_string(...) const
{
return string::format("%p", &m_o);
}
const T& m_o;
};
template <typename T> std::string as_string(const T& o)
{
return repr_type<T>(o).as_string();
}
My Question is why it is typecasted to repr_type, in that global templated function as_string(). In the article itself, they mentioned reason as " I cannot also pass in the object instance: so I need a proxy object ". But I couldn't understand. Please explain with other simple example.

X is not a valid template argument for 'const char*' because it is not the address of a variable

I'm trying to use the resources of a temporary class object as a template parameter. But apparently this doesn't work:
godbolt
#include <iostream>
constexpr size_t size(const char* s)
{
int i = 0;
while(*s!=0) {
++i;
++s;
}
return i;
}
template <const char* S>
struct string_struct
{
constexpr string_struct() {
for (int i=0; i < size(S); ++i) {
buf_[i] = S[i];
}
}
constexpr const char* get() {
return buf_;
}
char buf_[size(S)] = {0};
};
constexpr const char some_chars[] = "100";
constexpr auto compose_string()
{
string_struct<some_chars> other_str{};
return string_struct<other_str.get()>;
}
int main()
{
compose_string();
}
gcc 12.1 complains:
<source>: In function 'constexpr auto compose_string()':
<source>:32:41: error: 'other_str.string_struct<(& some_chars)>::get()' is not a valid template argument for 'const char*' because it is not the address of a variable
32 | return string_struct<other_str.get()>;
| ^
<source>:29:16: error: invalid return type 'auto' of 'constexpr' function 'constexpr auto compose_string()'
29 | constexpr auto compose_string()
|
Of course this is a contrived example. What I actually want to do is extend an enhanced version of string_struct recursively. But how can I use its resources for template instantiation? And if that is somehow possible, how do I deduce the return type of a function that returns string_structs (and in the recursive case a string_struct, that is instantiated with another string_struct, that is instantiated with another sstring_struct...)?
Is it even possible?

Boost.Container flat_map and std::string_view

For some time I’ve used Boost’s flat_map as my go-to associative collection, for the reasons explained cited on their documentation intro, and (originally) the fact that it gave newer features before the compiler’s std implementation, and it was the same across platforms.
Now, I wanted to start using string_view to prevent copying strings, when these are taken from substrings of a larger input. string_view points to a range of characters within the larger string, without having to copy them into a new std::string instance.
In reaching for a map to use, I recalled that another progressive feature of Boost.Container that I’ve enjoyed in the past is conformal keys, where you could use anything that compared correctly against the stored key, rather than converting to the actual type of key.
But now I can’t find any mention of that in the documentation. I know the std::map can do that now (since C++14) but I’d rather use the flat_map for tiny collections.
What could I have seen that allowed this flexibility, years ago, if it’s not apparent in boost::flat_map::insert etc.? What are good flat collections to use now with up-to-date compilers?
Support for polymorphic lookup functions has been added only recently to Boost.Container. If everything is good, it should be released with Boost 1.68.
In the meantime you can emulate flat associative containers with an ordered std::vector and std::lower_bound.
typedef std::pair< std::string, int > element_type;
std::vector< element_type > map;
struct element_order
{
bool operator()(element_type const& left, element_type const& right) const
{
return left.first < right.first;
}
bool operator()(std::string_view const& left, element_type const& right) const
{
return left < right.first;
}
bool operator()(element_type const& left, std::string_view const& right) const
{
return left.first < right;
}
};
auto find_element(std::string_view const& key)
{
auto it = std::lower_bound(map.begin(), map.end(), key, element_order());
if (it != map.end() && it->first == key)
return it;
return map.end();
}
Perhaps this is not what you are referring to, but if you use std::string_view as the key type, all operations already work via the implicit conversion to std::string_view:
Live On Coliru
#include <boost/container/flat_map.hpp>
#include <string_view>
int main() {
boost::container::flat_map<std::string_view, int> m {
{ "one", 1 },
{ "two", 2 },
{ "three", 3 },
{ "four", 4 },
};
std::string key = "one";
auto one = m.at(key);
auto range = m.equal_range(key);
auto it = m.find(key);
m[key] = 1;
}
The Inverse
Here you'd actually need to use a container that supports compatible-key lookup indeed. It doesn't need to be overly complicated to roll one:
Here's one:
Live On Coliru
#include <initializer_list>
#include <algorithm>
#include <utility>
#include <stdexcept>
#include <boost/container/small_vector.hpp>
template <typename K, typename V, typename Cmp = std::less<K>, typename Storage = boost::container::small_vector<std::pair<K, V>, 10> >
struct flat_map {
using key_type = K;
using mapped_type = V;
using key_compare = Cmp;
using storage = Storage;
using value_type = typename storage::value_type;
using iterator = typename Storage::iterator;
using const_iterator = typename Storage::const_iterator;
struct value_compare {
key_compare _cmp;
template <typename A, typename B>
bool operator()(A const& a, B const& b) const { return _cmp(access(a), access(b)); }
private:
static auto& access(value_type const& v) { return v.first; }
template <typename Other>
static auto& access(Other const& v) { return v; }
} _cmp;
storage _data;
flat_map(std::initializer_list<value_type> i) : _data(i) {}
iterator begin() { return _data.begin(); }
iterator end() { return _data.end(); }
const_iterator begin() const { return _data.begin(); }
const_iterator end() const { return _data.end(); }
template <typename Key>
mapped_type& operator[](Key&& key) { return find(std::forward<Key>(key))->second; }
template <typename Key>
mapped_type const& operator[](Key&& key) const { return find(std::forward<Key>(key))->second; }
template <typename Key>
iterator find(Key&& key) {
auto r = equal_range(std::forward<Key>(key));
return (r.first == r.second)? end() : r.first;
}
template <typename Key>
const_iterator find(Key&& key) const {
auto r = equal_range(std::forward<Key>(key));
return (r.first == r.second)? end() : r.first;
}
template <typename Key>
mapped_type& at(Key&& key) {
auto r = equal_range(std::forward<Key>(key));
if (r.first == r.second) throw std::out_of_range("key");
return r.first->second;
}
template <typename Key>
mapped_type const& at(Key&& key) const {
auto r = equal_range(std::forward<Key>(key));
if (r.first == r.second) throw std::out_of_range("key");
return r.first->second;
}
template <typename Key>
auto equal_range(Key&& key) { return std::equal_range(begin(), end(), std::forward<Key>(key), _cmp); }
template <typename Key>
auto equal_range(Key&& key) const { return std::equal_range(begin(), end(), std::forward<Key>(key), _cmp); }
};
It supports precisely the inverse of the first scenario (given the comparator of std::less<>):
#include <string_view>
#include <string>
int main() {
flat_map<std::string, int, std::less<> > m {
{ "one", 1 },
{ "two", 2 },
{ "three", 3 },
{ "four", 4 },
};
std::string_view key = "one";
auto one = m.at(key);
auto range = m.equal_range(key);
auto it = m.find(key);
m[key] = 1;
}

Call QtDBus method with array-of-strings arguments

I try to call a method with arguments.
For example, list services.
This code
#include <QtDBus>
#include <QDebug>
QDBusMessage callDbusMethod(QString method, QList<QVariant> args= {}) {
QDBusMessage msg;
if(QDBusConnection::systemBus().isConnected()) {
QDBusInterface iface("org.freedesktop.systemd1", "/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager", QDBusConnection::systemBus());
if(iface.isValid())
msg= args.isEmpty() ? iface.call(QDBus::AutoDetect, method.toLatin1())
: iface.callWithArgumentList(QDBus::AutoDetect, method.toLatin1(), args);
if(msg.type() == QDBusMessage::ErrorMessage)
qDebug() << msg.errorMessage(); }
return msg;
}
typedef struct {
QString path, state; } UnitFile;
int main() {
QDBusMessage msg= callDbusMethod("ListUnitFilesByPatterns",
QList<QVariant>{ QVariant(""), QVariant("*.service") });
//QDBusMessage msg= callDbusMethod("ListUnitFiles");
if(msg.arguments().size()) {
const QDBusArgument argUnitFiles= msg.arguments().at(0).value<QDBusArgument>();
argUnitFiles.beginArray();
while(!argUnitFiles.atEnd()) {
UnitFile unit;
argUnitFiles.beginStructure();
argUnitFiles >> unit.path >> unit.state;
argUnitFiles.endStructure();
qDebug() << unit.path; }
argUnitFiles.endArray(); }
}
Return
"Invalid arguments 'ss' to call org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns(), expecting 'asas'."
And how to call the method correctly.
You already answered your question in the comments. But I figured I'd explain more here.
In the submission you are passing the argument list of ss since your QList has two QVariants which are strings. The signature of org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns is asas or two array of strings. According to qt's dbus typesystem, as is automatically de-marshaled into a QStringList. Therefore you'd want a QList of QVariants which are QStringLists.
So..
QList<QVariant> args = { QVariant(""), QVariant("*.service") };
should be
QList<QVariant> args = { QStringList() << "", QStringList() << "*.service" };

Constant container (map) - eliminate heap allocation

If I create a static const std::map, it will allocate memory on heap. Following code throws bad_alloc:
#include <iostream>
#include <map>
class A {
public:
static const std::map<int, int> a;
};
const std::map<int, int> A::a = { { 1, 3} , { 2, 5} };
void* operator new ( std::size_t count )
{
throw std::bad_alloc();
}
int
main (void)
{
for(auto &ai: A::a) {
std::cout << ai.first << " " << ai.second << "\n";
}
return 0;
}
Is it possible to create this constant map somehow without having memory allocation?
As Igor Tandetnik suggested, a custom allocator would do the trick. The following is a quick'n'dirty example of a simple linear allocator, which returns memory slots from a static buffer:
#include <iostream>
#include <map>
#include <cassert>
template <typename T>
class LinearAllocator {
static constexpr size_t _maxAlloc = 1<<20;
using Buffer = std::array<T, _maxAlloc>;
using FreeList = std::array<bool, _maxAlloc>;
static Buffer _buffer;
static FreeList _allocated;
public:
typedef T* pointer;
typedef T value_type;
template<typename U>
struct rebind { typedef LinearAllocator<U> other; };
pointer allocate(size_t /*n*/, const void *hint=0) {
for(size_t i = 0; i < _maxAlloc; ++i) {
if(!_allocated[i]) {
_allocated[i] = true;
return &_buffer[i];
}
}
throw std::bad_alloc();
}
void deallocate(pointer p, size_t /*n*/) {
assert(p >= &_buffer[0] && p < &_buffer[_maxAlloc]);
_allocated[p-&_buffer[0]] = false;
}
LinearAllocator() throw() { }
LinearAllocator(const LinearAllocator &a) throw() { }
template <class U>
LinearAllocator(const LinearAllocator<U> &a) throw() { }
~LinearAllocator() throw() { }
};
template <typename T>
typename LinearAllocator<T>::Buffer LinearAllocator<T>::_buffer;
template <typename T>
typename LinearAllocator<T>::FreeList LinearAllocator<T>::_allocated;
using MyMap = std::map<int, int, std::less<int>,
LinearAllocator<std::pair<int,int> > >;
// make sure we notice if new gets called
void* operator new(size_t size) {
std::cout << "new called" << std::endl;
}
int main() {
MyMap m;
m[0] = 1; m[1] = 3; m[2] = 8;
for(auto & p : m)
std::cout << p.first << ": " << p.second << std::endl;
return 0;
}
Output:
0: 1
1: 3
2: 8
Note that this allocator will only handle requests for single slots at a time. I'm sure you will figure out how to extend it according to your requirements.

Resources