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.
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?
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;
}
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" };
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.