Related
Obviously we can concatenate two string literals in a constexpr function, but what about concatenation of a string literal with a string returned by another constexpr function as in the code below?
template <class T>
constexpr const char * get_arithmetic_size()
{
switch (sizeof(T))
{
case 1: return "1";
case 2: return "2";
case 4: return "4";
case 8: return "8";
case 16: return "16";
default: static_assert(dependent_false_v<T>);
}
}
template <class T>
constexpr std::enable_if_t<std::is_arithmetic_v<T>, const char *> make_type_name()
{
const char * prefix = std::is_signed_v<T> ? "int" : "uint";
return prefix; // how to concatenate prefix with get_arithmetic_size<T>() ?
}
static_assert(strings_equal(make_type_name<int>, make_type_name<int32_t>);
The code makes compiler-independent string identifier of an arithmetic type.
EDIT1:
A little bit more complicated example is:
template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};
template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
template <class T>
constexpr std::enable_if_t<is_specialization<T, std::vector>::value || is_specialization<T, std::list>::value, const char *> make_type_name()
{
return "sequence"; // + make_type_name<typename T::value_type>;
}
static_assert(strings_equal(make_type_name<std::vector<int>>(), make_type_name<std::list<int>>()));
Here is a quick compile time string class:
template<std::size_t N>
struct ct_str
{
char state[N+1] = {0};
constexpr ct_str( char const(&arr)[N+1] )
{
for (std::size_t i = 0; i < N; ++i)
state[i] = arr[i];
}
constexpr char operator[](std::size_t i) const { return state[i]; }
constexpr char& operator[](std::size_t i) { return state[i]; }
constexpr explicit operator char const*() const { return state; }
constexpr char const* data() const { return state; }
constexpr std::size_t size() const { return N; }
constexpr char const* begin() const { return state; }
constexpr char const* end() const { return begin()+size(); }
constexpr ct_str() = default;
constexpr ct_str( ct_str const& ) = default;
constexpr ct_str& operator=( ct_str const& ) = default;
template<std::size_t M>
friend constexpr ct_str<N+M> operator+( ct_str lhs, ct_str<M> rhs )
{
ct_str<N+M> retval;
for (std::size_t i = 0; i < N; ++i)
retval[i] = lhs[i];
for (std::size_t i = 0; i < M; ++i)
retval[N+i] = rhs[i];
return retval;
}
friend constexpr bool operator==( ct_str lhs, ct_str rhs )
{
for (std::size_t i = 0; i < N; ++i)
if (lhs[i] != rhs[i]) return false;
return true;
}
friend constexpr bool operator!=( ct_str lhs, ct_str rhs )
{
for (std::size_t i = 0; i < N; ++i)
if (lhs[i] != rhs[i]) return true;
return false;
}
template<std::size_t M, std::enable_if_t< M!=N, bool > = true>
friend constexpr bool operator!=( ct_str lhs, ct_str<M> rhs ) { return true; }
template<std::size_t M, std::enable_if_t< M!=N, bool > = true>
friend bool operator==( ct_str, ct_str<M> ) { return false; }
};
template<std::size_t N>
ct_str( char const(&)[N] )->ct_str<N-1>;
you can use it like this:
template <class T>
constexpr auto get_arithmetic_size()
{
if constexpr (sizeof(T)==1)
return ct_str{"1"};
if constexpr (sizeof(T)==2)
return ct_str{"2"};
if constexpr (sizeof(T)==4)
return ct_str{"4"};
if constexpr (sizeof(T)==8)
return ct_str{"8"};
if constexpr (sizeof(T)==16)
return ct_str{"16"};
}
template <class T, std::enable_if_t<std::is_arithmetic<T>{}, bool> = true>
constexpr auto make_type_name()
{
if constexpr (std::is_signed<T>{})
return ct_str{"int"} + get_arithmetic_size<T>();
else
return ct_str{"uint"} + get_arithmetic_size<T>();
}
which leads to statements like:
static_assert(make_type_name<int>() == make_type_name<int32_t>());
passing.
Live example.
Now one annoying thing is that the length of the buffer is in the type system. You could add a length field, and make N be "buffer size", and modify ct_str to only copy up to length and leave the trailing bytes as 0. Then override common_type to return the max N of both sides.
That would permit you do pass ct_str{"uint"} and ct_str{"int"} in the same type of value and make the implementation code a bit less annoying.
template<std::size_t N>
struct ct_str
{
char state[N+1] = {0};
template<std::size_t M, std::enable_if_t< (M<=N+1), bool > = true>
constexpr ct_str( char const(&arr)[M] ):
ct_str( arr, std::make_index_sequence<M>{} )
{}
template<std::size_t M, std::enable_if_t< (M<N), bool > = true >
constexpr ct_str( ct_str<M> const& o ):
ct_str( o, std::make_index_sequence<M>{} )
{}
private:
template<std::size_t M, std::size_t...Is>
constexpr ct_str( char const(&arr)[M], std::index_sequence<Is...> ):
state{ arr[Is]... }
{}
template<std::size_t M, std::size_t...Is>
constexpr ct_str( ct_str<M> const& o, std::index_sequence<Is...> ):
state{ o[Is]... }
{}
public:
constexpr char operator[](std::size_t i) const { return state[i]; }
constexpr char& operator[](std::size_t i) { return state[i]; }
constexpr explicit operator char const*() const { return state; }
constexpr char const* data() const { return state; }
constexpr std::size_t size() const {
std::size_t retval = 0;
while(state[retval]) {
++retval;
}
return retval;
}
constexpr char const* begin() const { return state; }
constexpr char const* end() const { return begin()+size(); }
constexpr ct_str() = default;
constexpr ct_str( ct_str const& ) = default;
constexpr ct_str& operator=( ct_str const& ) = default;
template<std::size_t M>
friend constexpr ct_str<N+M> operator+( ct_str lhs, ct_str<M> rhs )
{
ct_str<N+M> retval;
for (std::size_t i = 0; i < lhs.size(); ++i)
retval[i] = lhs[i];
for (std::size_t i = 0; i < rhs.size(); ++i)
retval[lhs.size()+i] = rhs[i];
return retval;
}
template<std::size_t M>
friend constexpr bool operator==( ct_str lhs, ct_str<M> rhs )
{
if (lhs.size() != rhs.size()) return false;
for (std::size_t i = 0; i < lhs.size(); ++i)
if (lhs[i] != rhs[i]) return false;
return true;
}
template<std::size_t M>
friend constexpr bool operator!=( ct_str lhs, ct_str<M> rhs )
{
if (lhs.size() != rhs.size()) return true;
for (std::size_t i = 0; i < lhs.size(); ++i)
if (lhs[i] != rhs[i]) return true;
return false;
}
};
template<std::size_t N>
ct_str( char const(&)[N] )->ct_str<N-1>;
The function implementations now become:
template <class T>
constexpr ct_str<2> get_arithmetic_size()
{
switch (sizeof(T)) {
case 1: return "1";
case 2: return "2";
case 4: return "4";
case 8: return "8";
case 16: return "16";
}
}
template <class T, std::enable_if_t<std::is_arithmetic<T>{}, bool> = true>
constexpr auto make_type_name()
{
constexpr auto base = std::is_signed<T>{}?ct_str{"int"}:ct_str{"uint"};
return base + get_arithmetic_size<T>();
}
which is a lot more natural to write.
Live example.
No, it's impossible. You can implement something like below (it's C++14).
#include <cmath>
#include <cstring>
#include <iostream>
#include <type_traits>
constexpr const char* name[] = {
"uint1", "uint2", "uint4", "uint8", "uint16",
"int1", "int2", "int4", "int8", "int16"
};
template <class T>
constexpr std::enable_if_t<std::is_arithmetic<T>::value, const char *> make_type_name() {
return name[std::is_signed<T>::value * 5 +
static_cast<int>(std::log(sizeof(T)) / std::log(2) + 0.5)];
}
static_assert(std::strcmp(make_type_name<int>(), make_type_name<int32_t>()) == 0);
int main() {
std::cout << make_type_name<int>();
return 0;
}
https://ideone.com/BaADaM
If you don't like using <cmath>, you may replace std::log:
#include <cstring>
#include <iostream>
#include <type_traits>
constexpr const char* name[] = {
"uint1", "uint2", "uint4", "uint8", "uint16",
"int1", "int2", "int4", "int8", "int16"
};
constexpr size_t log2(size_t n) {
return (n<2) ? 0 : 1 + log2(n/2);
}
template <class T>
constexpr std::enable_if_t<std::is_arithmetic<T>::value, const char *> make_type_name() {
return name[std::is_signed<T>::value * 5 + log2(sizeof(T))];
}
static_assert(std::strcmp(make_type_name<int>(), make_type_name<int32_t>()) == 0);
int main() {
std::cout << make_type_name<int>();
return 0;
}
I have little c++ experience, but now I need to look at some code that uses expression templates a lot, so I am reading chapter 18 of the book << C++ Templates: The Complete Guide >> and working on the example provided in the book. If you happened to have the book, the example starts from pp 328, with all the contextual information.
My code works fine until I want to add the support for subvector indexing (pp 338), I could not get the assignment to work, g++ gives the following error:
error: binding ‘const value_type {aka const double}’ to reference of type ‘double&’ discards qualifiers
return v[vi[idx]];
I have no idea what's going on, am I assigning to a constant object? How do I make this work? Here is my code:
#include <iostream>
#include <vector>
template<typename T>
class ET_Scalar {
private:
const T& s;
public:
ET_Scalar(const T& v) :
s(v) {}
T operator[](size_t) const
{
return s;
}
size_t size() const
{
return 0; // Zero means it's a scalar
}
};
template<typename T, typename V, typename VI>
class ET_SubVec {
private:
const V& v;
const VI& vi;
public:
ET_SubVec(const V& a, const VI& b) :
v(a), vi(b) {}
const T operator[] (size_t idx) const
{
return v[vi[idx]];
}
T& operator[] (size_t idx)
{
return v[vi[idx]];
}
size_t size() const
{
return vi.size();
}
};
// Using std::vector as storage
template<typename T, typename Rep = std::vector<T>>
class ET_Vector {
private:
Rep expr_rep;
public:
// Create vector with initial size
explicit ET_Vector(size_t s) :
expr_rep(s) {}
ET_Vector(const Rep& v) :
expr_rep(v) {}
ET_Vector& operator=(const ET_Vector& v)
{
for (size_t i = 0; i < v.size(); i++)
expr_rep[i] = v[i];
return *this;
}
template<typename T2, typename Rep2>
ET_Vector& operator=(const ET_Vector<T2, Rep2>& v)
{
for (size_t i = 0; i < v.size(); i++)
expr_rep[i] = v[i];
return *this;
}
size_t size() const
{
return expr_rep.size();
}
const T operator[](size_t idx) const
{
return expr_rep[idx];
}
T& operator[](size_t idx)
{
return expr_rep[idx];
}
template<typename T2, typename Rep2>
ET_Vector<T, ET_SubVec<T, Rep, Rep2>> operator[](const ET_Vector<T2, Rep2>& vi)
{
return ET_Vector<T, ET_SubVec<T, Rep, Rep2>>(ET_SubVec<T, Rep, Rep2>(expr_rep, vi.rep()));
}
template<typename T2, typename Rep2>
const ET_Vector<T, ET_SubVec<T, Rep, Rep2>> operator[](const ET_Vector<T2, Rep2>& vi) const
{
return ET_Vector<T, ET_SubVec<T, Rep, Rep2>>(ET_SubVec<T, Rep, Rep2>(expr_rep, vi.rep()));
}
// Return what the vector currently represents
const Rep& rep() const
{
return expr_rep;
}
Rep& rep()
{
return expr_rep;
}
};
template<typename T>
class ET_Traits {
public:
typedef const T& ExprRef;
};
template<typename T>
class ET_Traits<ET_Scalar<T>> {
public:
typedef ET_Scalar<T> ExprRef;
};
template<typename T, typename LHS, typename RHS>
class ET_Add {
private:
typename ET_Traits<LHS>::ExprRef lhs;
typename ET_Traits<RHS>::ExprRef rhs;
public:
ET_Add(const LHS& l, const RHS& r) :
lhs(l), rhs(r) {}
T operator[](size_t idx) const
{
return lhs[idx] + rhs[idx];
}
size_t size() const
{
return (lhs.size() != 0) ? lhs.size() : rhs.size();
}
};
template<typename T, typename LHS, typename RHS>
class ET_Mul {
private:
typename ET_Traits<LHS>::ExprRef lhs;
typename ET_Traits<RHS>::ExprRef rhs;
public:
ET_Mul(const LHS& l, const RHS& r) :
lhs(l), rhs(r) {}
T operator[](size_t idx) const
{
return lhs[idx] * rhs[idx];
}
size_t size() const
{
return (lhs.size() != 0) ? lhs.size() : rhs.size();
}
};
// Vector + Vector
template<typename T, typename LHS, typename RHS>
ET_Vector<T, ET_Add<T, LHS, RHS>>
operator+(const ET_Vector<T, LHS>& a, const ET_Vector<T, RHS>& b)
{
return ET_Vector<T, ET_Add<T, LHS, RHS>>(ET_Add<T, LHS, RHS>(a.rep(), b.rep()));
}
// Scalar + Vector
template<typename T, typename RHS>
ET_Vector<T, ET_Add<T, ET_Scalar<T>, RHS>>
operator+(const T& s, const ET_Vector<T, RHS>& b)
{
return ET_Vector<T, ET_Add<T, ET_Scalar<T>, RHS>>(ET_Add<T, ET_Scalar<T>, RHS>(ET_Scalar<T>(s), b.rep()));
}
// Vector .* Vector
template<typename T, typename LHS, typename RHS>
ET_Vector<T, ET_Mul<T, LHS, RHS>>
operator*(const ET_Vector<T, LHS>& a, const ET_Vector<T, RHS>& b)
{
return ET_Vector<T, ET_Mul<T, LHS, RHS>>(ET_Mul<T, LHS, RHS>(a.rep(), b.rep()));
}
//Scalar * Vector
template<typename T, typename RHS>
ET_Vector<T, ET_Mul<T, ET_Scalar<T>, RHS>>
operator*(const T& s, const ET_Vector<T, RHS>& b)
{
return ET_Vector<T, ET_Mul<T, ET_Scalar<T>, RHS>>(ET_Mul<T, ET_Scalar<T>, RHS>(ET_Scalar<T>(s), b.rep()));
}
template<typename T>
void print_vec(const T& e)
{
for (size_t i = 0; i < e.size(); i++) {
std::cout << e[i] << ' ';
}
std::cout << '\n';
return;
}
int main()
{
size_t N = 16;
ET_Vector<double> x(N);
ET_Vector<double> y(N);
ET_Vector<double> z(N);
ET_Vector<int> idx(N / 2);
// Do not use auto z = [expr] here! Otherwise the type of z will still be a
// container, and evaluation won't happen until later. But the compiler
// will optimize necessary information away, causing errors.
z = (6.5 + x) + (-2.0 * (1.25 + y));
print_vec(z);
for (int i = 0; i < 8; i++)
idx[i] = 2 * i;
z[idx] = -1.0 * z[idx];
print_vec(z);
return 0;
}
Sorry about its length, I've failed to create a minimal (not) working example.
Let's say we store a struct with a string key and we want to find it by that string in a container like std::set, so common implementation would look like this:
struct Foo {
std::string id;
};
struct FooComp {
using is_transparent = std::true_type;
bool operator()( const Foo &foo, const std::string &str ) const
{
return foo.id < str;
}
bool operator()( const std::string &str, const Foo &foo ) const
{
return str < foo.id;
}
bool operator()( const Foo &foo1, const Foo &foo2 ) const
{
return foo1.id < foo2.id;
}
};
std::set<Foo,FooComp> foo_set;
...
This works fine, but writing three methods for FooComp that do prety match the same (logically) is monotonic and error prone. Is there a way to minimize that code?
You may do as following:
struct Foo {
std::string id;
};
struct FooComp {
using is_transparent = std::true_type;
template <typename LHS, typename RHS>
bool operator()(const LHS& lhs, const RHS& rhs) const
{
return ProjectAsId(lhs) < ProjectAsId(rhs);
}
private:
const std::string& ProjectAsId(const std::string& s) const { return s; }
const std::string& ProjectAsId(const Foo& foo) const { return foo.id; }
};
You write comparison once, but you have to write the projection for each type.
In C++17, it can even be
template <auto f> struct ProjLess
{
using is_transparent = std::true_type;
template <typename LHS, typename RHS>
bool operator()(const LHS& lhs, const RHS& rhs) const
{
return project(lhs) < project(rhs);
}
private:
template <typename T>
using f_t = decltype(std::invoke(f, std::declval<const T&>()));
template <typename T>
using is_f_callable = is_detected<f_t, T>;
template <typename T, std::enable_if_t<is_f_callable<T>::value>* = nullptr>
decltype(auto) project(const T& t) const { return std::invoke(f, t); }
template <typename T, std::enable_if_t<!is_f_callable<T>::value>* = nullptr>
const T& project(const T& t) const { return t; }
};
And usage:
std::set<Foo, ProjLess<&Foo::id>> s;
Demo with C++17
My solution is all in the class:
struct FooComp {
using is_transparent = std::true_type;
struct FooProj {
std::string const& str;
FooProj( std::string const& sin ):str(sin) {}
FooProj( const Foo& foo ):str(foo.id) {}
FooProj( FooProj const& ) = default;
friend bool operator<(FooProj lhs, FooProj rhs) {
return lhs.str < rhs.str;
}
};
bool operator()( FooProj lhs, FooProj rhs ) const
{
return lhs<rhs;
}
};
This doesn't support types that can convert to std::string.
However, when doing a projection-based comparison, I do this:
template<class F, class After=std::less<>>
auto order_by( F&& f, After&& after={} ) {
return
[f=std::forward<F>(f), after=std::forward<After>(after)]
(auto&& rhs, auto&&lhs)->bool {
return after( f(decltype(lhs)(lhs)), f(decltype(rhs)(rhs)) );
};
}
which takes a projection and generates a comparison function for it. We make it transparent with:
template<class F>
struct as_transparent_t {
F f;
using is_transparent=std::true_type;
template<class Lhs, class Rhs>
bool operator(Lhs const& lhs, Rhs const& rhs)const{ return f(lhs, rhs); }
};
template<class F>
as_transparent_f<std::decay_t<F>>
as_transparent( F&& f ) { return {std::forward<F>(f)}; }
so we can project and be transparent via:
as_transparent( order_by( some_projection ) );
which only leaves the projection.
In C++14 we just do a
std::string const& foo_proj_f( std::string const& str ) { return str; }
std::string const& foo_proj_f( Foo const& foo ) { return foo.id; }
auto foo_proj = [](auto const& x)->decltype(auto){ return foo_proj_f(x); };
auto foo_order = as_transparent( order_by( foo_proj ) );
which breaks things down into modular chunks.
In C++17 we can use if constexpr:
auto foo_proj = [](auto const& x)->std::string const& {
if constexpr( std::is_same<decltype(x), std::string const&>{} ) {
return x;
}
if constexpr( std::is_same<decltype(x), Foo const&>{} ) {
return x.id;
}
};
auto foo_order = as_transparent( order_by( foo_proj ) );
or
template<class...Ts>
struct overloaded:Ts...{
using Ts::operator()...;
overloaded(Ts...ts):Ts(std::move(ts)...){}
};
template<class...Ts> overloaded -> overloaded<Ts...>;
which permits
auto foo_proj = overloaded{
[](std::string const& s)->decltype(auto){return s;},
[](Foo const& f)->decltype(auto){return f.id;}
};
which may be easier to read than the if constexpr version. (This version can also be adapted to c++14 or c++11).
I need to use a function template which is available in a header file via a function pointer which is available as a struct member.
For example:
File: mytemplate.h
template<typename T> const bool GT(const T& pa_roIN1, const T& pa_roIN2)
{
bool temp = false;
if(pa_roIN1 > pa_roIN2){
temp = true;
}
return temp;
}
template<typename T> const bool EQ(const T& pa_roIN1, const T& pa_roIN2)
{
bool temp = false;
if(pa_roIN1 == pa_roIN2){
temp = true;
}
return temp;
}
template<typename T> const bool GE(const T& pa_roIN1, const T& pa_roIN2)
{
bool temp = false;
if(pa_roIN1 >= pa_roIN2){
temp = true;
}
return temp;
}
File: mystruct.h:
struct mystruct {
std::string funcName;
bool (*funcPtr)(int,int);
};
typedef std::map<int, mystruct> mystructList;
File: mycpp.cpp:
#include "mytemplate.h"
#include "mystruct.h"
mystruct MyGreaterStruct[3] = {
{"GREATER_THAN", >},
{ "EQUAL", &EQ},
{ "GREATER_N_EQUAL", &GE}
};
mystructList MyList;
int main(void)
{
int size = sizeof(MyGreaterStruct)/sizeof(mystruct);
for(int i = 0; i < size; i++) {
MyList.insert(std::pair<int, mystruct>(i,MyGreaterStruct[i])); }
for(mystructList::iterator iter = MyList.begin(); iter != MyList.end(); iter++) {
printf("\nResult of func : %s for values 100,50 : %d",iter->second.funcName.c_Str(),iter->second->funcPtr(100,50));
}
}
I am not sure this the correct way of doing it. But there is no result that gets printed on the console.
The error you get when compiling your code tells you what is wrong:
mycpp.cpp:8:1: error: no matches converting function ‘GT’ to type ‘bool (*)(int, int)’
};
^
mytemplate.h:1:33: note: candidate is: template<class T> const bool GT(const T&, const T&)
template<typename T> const bool GT(const T& pa_roIN1, const T& pa_roIN2)
^
There's no way to instantiate that template to match the signature of the function pointer, as the template takes two references as arguments, while the pointer takes two ints. If you can't change either the struct or the templates, and you are using C++11, you could try using lambdas instead:
mystruct MyGreaterStruct[3] = {
{"GREATER_THAN", [](int a, int b)->bool { return GT(a, b); } },
{ "EQUAL", [](int a, int b)->bool { return EQ(a, b); } },
{ "GREATER_N_EQUAL", [](int a, int b)->bool { return GE(a, b); } }
};
but no matter how you slice it, you need to either change the arguments or use a little function that converts the arguments from one form to the other.
I am searching for template library with set-like container allowing searching by different key. I don't want map (key duplication) and want C++11 compliant code (C++14 added template<class K> iterator std::set::find(const K& x) which could be used in std::set<T*,my_transparent_deref_less<T*> > with custom compare functor).
Do you know such? Will boost add such or does it have already?
The signature should look like this: the_set<T, GetKey, Compare> and I want structure optimized for both size / memory usage (thus flat_set / btree_set) and speed of searching (insert/remove speed is not that critical). Example:
class User {
public:
User(const char *name);
const char *name();
... };
the_set<User*,const char*> USERS([](User* u) { u->name(); },
[](const char* lhs, const char* rhs) { strcmp(lhs, rhs) < 0; });
I have found red-black-tree in boost::detail that looks like what I want - the signature is template <class Key, class Value, class KeyOfValue, class KeyCompare, class A> class rbtree. Do we have something like that with flat_set and btree_set that I could use (without the fear of using something that is not to be used publicly but purposedly hidden as detail)?
Reason: I do plan to use such sets for many objects and many keys (possibly different keys/sets for same objects).
USERS, UNITS, ... - global using btree_set, possibly something like boost::multi_index
User::units, ... - sets in objects using flat_set
My code so far: (The problem is that I have to use StringWrapper now)
#include <set>
#include <iostream>
#include <type_traits>
#include "btree_set.h"
#include "boost/container/flat_set.hpp"
// dereferencing comparator
template <class T>
class less: public std::less<T> {
public:
typename std::enable_if<std::is_pointer<T>::value,
bool>::type operator() (T lhs, T rhs) const {
return *lhs < *rhs; }};
// here I can change underlying structure to btree_set or std::set
template <class T,
class C = less<T>,
class A = std::allocator<T> >
using default_set = boost::container::flat_set<T, C, A>;
// this works fine for classes derived from their primary key
template <class T, class K = T,
class B = default_set<K*> >
class object_set {
private:
typename std::enable_if<std::is_base_of<K, T>::value,
B>::type impl;
public:
template<class... Args>
T* add(K* key, Args&& ...args) {
auto it = impl.insert(key);
if (!it.second) return nullptr;
T* value = new T(*key, std::forward<Args>(args)...);
*it.first = value;
return value; }
T* operator[](K* key) {
auto it = impl.find(key);
if (it == impl.end()) return nullptr;
return (T*)*it; }
T* remove(K* key) {
auto it = impl.find(key);
if (it == impl.end()) return nullptr;
T* value = (T*)*it;
impl.erase(it);
return value; }
public:
template<class... Args>
T* add(K key, Args&& ...args) {
return add(&key, std::forward<Args>(args)...); }
T* operator[](K key) {
return (*this)[&key]; }
T* remove(K key) {
return remove(&key); }};
// workaround for above std::is_base_of constraint
class StringWrapper {
const char *data;
public:
StringWrapper(const char *data) {
this->data = data; }
operator const char *() const {
return data; }};
// example of class I want to use the container on
class User: public StringWrapper {
public:
User(const char *name): StringWrapper(name) {}};
// testing
object_set<User,StringWrapper> USERS;
int main() {
USERS.add("firda"); USERS.add("firda2");
User* firda = USERS["firda"];
delete USERS.remove(firda);
delete USERS.remove("firda2"); }
Sounds like a job for boost::multi_index
http://www.boost.org/doc/libs/1_55_0/libs/multi_index/doc/index.html
This is what I came with:
#include "boost/container/flat_set.hpp"
template<class T, class K, class GetKey, class CmpKey>
class fset {
private:
boost::container::container_detail::flat_tree<
K, T*, GetKey, CmpKey, std::allocator<T*> >
impl;
public:
template<class... Args>
T* add(K key, Args&& ...args) {
auto it = impl.lower_bound(key);
if (it != impl.end() && impl.key_comp()(key, GetKey()(*it))) {
return nullptr; }
T* value = new T(key, std::forward<Args>(args)...);
impl.insert_unique(it, value);
return value; }
T* operator[](K key) {
auto it = impl.find(key);
if (it == impl.end()) return nullptr;
return *it; }
T* remove(K key) {
auto it = impl.find(key);
if (it == impl.end()) return nullptr;
T* value = *it;
impl.erase(it);
return value; }};
class User {
private:
const char *name_;
public:
User(const char *name) {
std::size_t size = std::strlen(name) + 1;
char *buf = new char[size];
std::memcpy(buf, name, size);
name_ = buf; }
~User() {
delete[] name_; }
const char *name() const {
return name_; }
public:
struct get_name {
const char *operator()(User* u) const {
return u->name(); }};
struct cmp_name {
bool operator()(const char* lhs, const char* rhs) const {
return std::strcmp(lhs, rhs) < 0; }};};
fset<User,const char*,User::get_name,User::cmp_name>
USERS;
int main() {
USERS.add("firda");
User* firda = USERS["firda"];
delete USERS.remove("firda"); }
Should I close or delete this question now?
This is what I use now (look at struct vset_adaptor)
#ifndef HEADER___VECTSET___BE8EB41D7B3971E1
#define HEADER___VECTSET___BE8EB41D7B3971E1
#include <vector>
//############################################################### ptrvect
template <class T, class base = std::vector<T*> >
class ptrvect: public base {
public:
class iterator: public base::iterator {
friend class ptrvect;
private:
iterator(const typename base::const_iterator& it):
base::iterator(const_cast<T**>(&*it)) {
return; }
public:
iterator(const typename base::iterator& it):
base::iterator(it) {
return; }
T* operator->() const {
return **this; }};
class const_iterator: public base::const_iterator {
public:
const_iterator(const typename base::const_iterator& it):
base::const_iterator(it) {
return; }
const_iterator(const typename base::iterator& it):
base::const_iterator(it) {
return; }
T* operator->() const {
return **this; }};
template <class It = iterator>
class condpair: public std::pair<It,bool> {
public:
condpair(It it, bool second):
std::pair<It,bool>(it, second) {
return; }
T* operator->() const {
return *std::pair<It,bool>::first; }};
public:
iterator begin() {
return iterator(base::begin()); }
iterator end() {
return iterator(base::end()); }
const_iterator begin() const {
return const_iterator(base::begin()); }
const_iterator end() const {
return const_iterator(base::end()); }
public: // workarounds for pre-C++11 / bad C++11 implementation (should allow const_iterator)
iterator insert(const_iterator pos, T* value) {
return base::insert(iterator(pos), value); }
iterator erase(const_iterator pos) {
return base::erase(iterator(pos)); }
public: // addons
iterator find (T* key) {
return std::find(begin(), end(), key); }
const_iterator find (T* key) const {
return std::find(begin(), end(), key); }
bool contains (T* key) const {
return find(key) != end(); }
T* remove(T* key) {
auto it = find(key);
if (it == end()) return null;
T* val = *it;
base::erase(it);
return val; }
T* add(T* val) {
base::push_back(val);
return val; }
void release() {
for (T* it : *this) delete it;
base::clear(); }};
//########################################################## vset adaptor
template <class T, class K>
struct vset_adaptor {
K operator()(T* it) const {
return (K)(*it); }
bool operator()(K lhs, K rhs) const {
return lhs < rhs; }};
template <class T>
struct vset_adaptor<T,T*> {
T* operator()(T* it) const {
return it; }
bool operator()(T* lhs, T* rhs) const {
return lhs < rhs; }};
//================================================================== vset
template <class T, class K=T*, class F = vset_adaptor<T,K> >
class vset {
private:
ptrvect<T> impl;
struct Comp {
F f;
K operator()(T* it) const {
return f(it); }
bool operator()(K lhs, K rhs) const {
return f(lhs, rhs); }
bool operator()(T* lhs, K rhs) const {
return f(f(lhs), rhs); }
bool operator()(K lhs, T* rhs) const {
return f(lhs, f(rhs)); }
bool operator()(T* lhs, T* rhs) const {
return f(f(lhs), f(rhs)); }};
Comp comp;
public:
typedef typename ptrvect<T>::const_iterator iterator, const_iterator;
typedef unsigned size_type;
typedef T *value_type;
typedef K key_type;
typedef typename ptrvect<T>::template condpair<iterator> condpair;
public:
iterator begin() const {
return iterator(impl.begin()); }
iterator end() const {
return iterator(impl.end()); }
size_type size() const {
return impl.size(); }
bool empty() const {
return impl.empty(); }
public:
iterator lower_bound(K key) const {
return std::lower_bound(impl.begin(), impl.end(), key, comp); }
iterator upper_bound(K key) const {
return std::upper_bound(impl.begin(), impl.end(), key, comp); }
std::pair<iterator, iterator> equal_range(K key) const {
return std::equal_range(impl.begin(), impl.end(), key, comp); }
iterator find(K key) const {
iterator it = lower_bound(key);
return it == end() || comp(key, *it) ? end() : it; }
bool contains(K key) const {
iterator it = lower_bound(key);
return it != end() && !comp(key, *it); }
public:
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type lower_bound(T* key) const {
return std::lower_bound(impl.begin(), impl.end(), comp(key), comp); }
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type upper_bound(T* key) const {
return std::upper_bound(impl.begin(), impl.end(), comp(key), comp); }
typename std::enable_if<!std::is_same<T*,K>::value,
std::pair<iterator, iterator> >::type equal_range(T* key) const {
return std::equal_range(impl.begin(), impl.end(), comp(key), comp); }
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type find(T* key) const {
iterator it = lower_bound(comp(key));
return it == end() || comp(key, *it) ? end() : it; }
public:
template<class... Args>
condpair emplace(K key, Args&& ...args) {
iterator it = lower_bound(key);
if (it == end() || comp(key, *it)) {
return condpair(impl.insert(it,
new T(key, std::forward<Args>(args)...)), true); }
return condpair(it, false); }
iterator erase(iterator at) {
return impl.erase(at); }
public:
T* add(T* value) {
iterator it = lower_bound(value);
if (it == end() || comp(comp(value), *it)) {
impl.insert(it, value);
return value; }
return nullptr; }
template<class... Args>
T* add(K key, Args&& ...args) {
iterator it = lower_bound(key);
if (it == end() || comp(key, *it)) {
T* value = new T(key, std::forward<Args>(args)...);
impl.insert(it, value);
return value; }
return nullptr; }
T* get(K key) const {
iterator it = find(key);
return it == impl.end() ? nullptr : *it; }
T* operator[](K key) const {
return *emplace(key).first; }
T* remove(K key) {
iterator it = find(key);
if (it == impl.end()) return nullptr;
T* value = *it;
impl.erase(it);
return value; }
typename std::enable_if<!std::is_same<T*,K>::value,
T*>::type remove(T* key) {
return remove(comp(key)); }
void release() {
for (T* it : *this) {
delete it; }
impl.clear(); }
void clear() {
impl.clear(); }};
#endif
....if you wonder about the codestyling, it is output of my own preprocessor. This is the real code:
#include <vector>
//############################################################### ptrvect
template <class T, class base = std::vector<T*> >
class ptrvect: public base
public:
class iterator: public base::iterator
friend class ptrvect
private:
iterator(const typename base::const_iterator& it):
base::iterator(const_cast<T**>(&*it))
return
public:
iterator(const typename base::iterator& it):
base::iterator(it)
return
T* operator->() const
return **this
class const_iterator: public base::const_iterator
public:
const_iterator(const typename base::const_iterator& it):
base::const_iterator(it)
return
const_iterator(const typename base::iterator& it):
base::const_iterator(it)
return
T* operator->() const
return **this
template <class It = iterator>
class condpair: public std::pair<It,bool>
public:
condpair(It it, bool second):
std::pair<It,bool>(it, second)
return
T* operator->() const
return *std::pair<It,bool>::first
public:
iterator begin()
return iterator(base::begin())
iterator end()
return iterator(base::end())
const_iterator begin() const
return const_iterator(base::begin())
const_iterator end() const
return const_iterator(base::end())
public: // workarounds for pre-C++11 / bad C++11 implementation (should allow const_iterator)
iterator insert(const_iterator pos, T* value)
return base::insert(iterator(pos), value)
iterator erase(const_iterator pos)
return base::erase(iterator(pos))
public: // addons
iterator find (T* key)
return std::find(begin(), end(), key)
const_iterator find (T* key) const
return std::find(begin(), end(), key)
bool contains (T* key) const
return find(key) != end()
T* remove(T* key)
auto it = find(key)
if it == end(); return null
T* val = *it
base::erase(it)
return val
T* add(T* val)
base::push_back(val)
return val
void release()
for T* it : *this; delete it
base::clear()
//########################################################## vset adaptor
template <class T, class K>
struct vset_adaptor
K operator()(T* it) const
return (K)(*it)
bool operator()(K lhs, K rhs) const
return lhs < rhs
template <class T>
struct vset_adaptor<T,T*>
T* operator()(T* it) const
return it
bool operator()(T* lhs, T* rhs) const
return lhs < rhs
//================================================================== vset
template <class T, class K=T*, class F = vset_adaptor<T,K> >
class vset
private:
ptrvect<T> impl
struct Comp
F f
K operator()(T* it) const
return f(it)
bool operator()(K lhs, K rhs) const
return f(lhs, rhs)
bool operator()(T* lhs, K rhs) const
return f(f(lhs), rhs)
bool operator()(K lhs, T* rhs) const
return f(lhs, f(rhs))
bool operator()(T* lhs, T* rhs) const
return f(f(lhs), f(rhs))
Comp comp
public:
typedef typename ptrvect<T>::const_iterator iterator, const_iterator
typedef unsigned size_type
typedef T *value_type
typedef K key_type
typedef typename ptrvect<T>::template condpair<iterator> condpair
public:
iterator begin() const
return iterator(impl.begin())
iterator end() const
return iterator(impl.end())
size_type size() const
return impl.size()
bool empty() const
return impl.empty()
public:
iterator lower_bound(K key) const
return std::lower_bound(impl.begin(), impl.end(), key, comp)
iterator upper_bound(K key) const
return std::upper_bound(impl.begin(), impl.end(), key, comp)
std::pair<iterator, iterator> equal_range(K key) const
return std::equal_range(impl.begin(), impl.end(), key, comp)
iterator find(K key) const
iterator it = lower_bound(key)
return it == end() || comp(key, *it) ? end() : it
bool contains(K key) const
iterator it = lower_bound(key)
return it != end() && !comp(key, *it)
public:
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type lower_bound(T* key) const
return std::lower_bound(impl.begin(), impl.end(), comp(key), comp)
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type upper_bound(T* key) const
return std::upper_bound(impl.begin(), impl.end(), comp(key), comp)
typename std::enable_if<!std::is_same<T*,K>::value,
std::pair<iterator, iterator> >::type equal_range(T* key) const
return std::equal_range(impl.begin(), impl.end(), comp(key), comp)
typename std::enable_if<!std::is_same<T*,K>::value,
iterator>::type find(T* key) const
iterator it = lower_bound(comp(key))
return it == end() || comp(key, *it) ? end() : it
public:
template<class... Args>
condpair emplace(K key, Args&& ...args)
iterator it = lower_bound(key)
if it == end() || comp(key, *it)
return condpair(impl.insert(it,
new T(key, std::forward<Args>(args)...)), true)
return condpair(it, false)
iterator erase(iterator at)
return impl.erase(at)
public:
T* add(T* value)
iterator it = lower_bound(value)
if it == end() || comp(comp(value), *it)
impl.insert(it, value)
return value
return nullptr
template<class... Args>
T* add(K key, Args&& ...args)
iterator it = lower_bound(key)
if it == end() || comp(key, *it)
T* value = new T(key, std::forward<Args>(args)...)
impl.insert(it, value)
return value
return nullptr
T* get(K key) const
iterator it = find(key)
return it == impl.end() ? nullptr : *it
T* operator[](K key) const
return *emplace(key).first
T* remove(K key)
iterator it = find(key)
if it == impl.end(); return nullptr
T* value = *it
impl.erase(it)
return value
typename std::enable_if<!std::is_same<T*,K>::value,
T*>::type remove(T* key)
return remove(comp(key))
void release()
for T* it : *this
delete it
impl.clear()
void clear()
impl.clear()
You already mentioned c++14 and the template find function. Here is a simple example for who is interested in it:
#include <iostream>
#include <set>
#include <vector>
using namespace std;
class Info
{
int num_;
public:
explicit Info(int n) : num_(n) {}
bool operator<(const Info &other) const { return num_ < other.num_; }
friend inline bool operator<(const Info& lhs, const int& rhs);
friend bool operator<(const int& lhs, const Info& rhs);
};
inline bool operator<(const Info& lhs, const int& rhs) { return lhs.num_ < rhs; }
inline bool operator<(const int& lhs, const Info& rhs) { return lhs < rhs.num_; }
int main()
{
// less<> is a is_transparent comparer
set<Info, less<> > s;
// insert two items
s.insert(Info(2));
s.insert(Info(4));
// search
for (auto n : {1,2,3,4,5}) {
cout << "Searching " << n << (s.find(n) == s.end()?" not found":" found") << endl;
}
return 0;
}
Result:
Searching 1 not found
Searching 2 found
Searching 3 not found
Searching 4 found
Searching 5 not found