Using predicates in custom implemented data structures - c++

I have a cusom implemented Heap for priority queue application
class Heap {
SomeCutsomClass elements[100];
....
....
};
Now I need to support two different comparison operation between the keys of the heap and I want to implement it using c++ predicates
struct less1
{
bool operator()(const SomeCutsomClass& c1, const SomeCutsomClass& c1)
{
//specific implementation
}
};
struct less2
{
bool operator()(const SomeCutsomClass& c1, const SomeCutsomClass& c1)
{
//specific implementation
}
};
Ideally I should be able to pass the predicates somehow in the constructor of Heap
I am not able to visualize how to achieve this.
It is not that I want to use only predicates.Since I cant overload same operator twice in SomeCutsomClass , I am trying to use predicates.
I tried looking up STL source code of some well known data structures like std::sort etc , but they look complicated to me.

Here is an example containing both an example of std::less use and custom predicates as functions (lambdas).
#include <cassert>
#include <array>
#include <iostream>
#include <functional>
// Make heap a class template
// this allows you to specialize easily for any class
// I added the STL like third template parameter as example
// type_t is the type to store in the heap
// N is the size of the heap
template<typename type_t, std::size_t N, class Compare = std::less<type_t>>
class Heap
{
public:
using pred_t = std::function<bool(const type_t&, const type_t&)>;
Heap() :
m_predicate{ Compare() }
{
}
explicit Heap(pred_t predicate) :
m_predicate{ predicate }
{
}
bool compare(const int a, const int b)
{
return m_predicate(a, b);
}
private:
std::array<type_t, N> m_elements;
pred_t m_predicate;
};
struct SomeCustomClass
{
// all you need to make SomeCustomClass usable in Heap
// if it uses std::less as third template parameter
// is to provide this compare overload
bool operator<(const SomeCustomClass& other) const
{
return m_value < other.m_value;
}
int m_value{};
};
int main()
{
// this heap will use std::less
Heap<int, 100> less_heap;
assert(less_heap.compare(1, 2));
// create a lambda predicate
auto pred = [](const int& lhs, const int& rhs)
{
return lhs > rhs;
};
// this heap will use custom predciate
Heap<int, 100> greater_heap(pred);
assert(greater_heap.compare(2,1));
//
Heap<SomeCustomClass, 10> custom_heap;
return 0;
}

Related

Is it possible to have a fixed size array as a unordered_map key

I am wondering if it is possible to have a std::unordered_map use a fixed size array as a key. For example, here is a simple cache that holds strings as the value but needs a uint8_t[] as the key:
using UserKeyV1 = uint8_t[16];
using UserKeyV2 = uint8_t[32];
template <typename T>
class StringCache
{
public:
bool addString(const T &userKey, const std::string &value)
{
auto [it, result] = m_cache.try_emplace(userKey, value);
// ^^^^^^^ this line won't compile: array initializer must be an initializer list or string literal
}
private:
struct EqualToFn {
bool operator()(const T &left, const T &right) const {
return std::memcmp(&left[0],
&right[0],
sizeof(T)) == 0;
}
};
struct HashFn {
size_t operator()(const T &k) const {
return std::_Hash_impl::hash(k);
}
};
std::unordered_map<T, std::string, HashFn, EqualToFn> cache_;
}
And in use would be something like:
StringCache<UserKey1> cache1;
uint8_t uniqueKey[16]; // this key was provided by 3rd party lib
cache1.addString(uniqueKey, strUsername)
This won't compile due to the error listed above but I'm not sure why. I created the custom hasher and equality functions for the array so that it knew how to handle such a key. I could probably solve this with std::array and copy the key to it first but wanted to avoid that if possible as it would involve a copy and this code will be potentially called 1000s of times a second.
Is what I am trying to achieve possible or do I just use std::array as a key?
it works fine with std::array
#include <cstdlib>
#include <iostream>
#include <unordered_map>
#include <cstring>
#include <array>
using UserKeyV1 = std::array<unsigned char,16>;
template <typename T>
class StringCache
{
public:
bool addString(const T &userKey, const std::string &value)
{
auto [it, result] = m_cache.try_emplace(userKey, value);
// ^^^^^^^ this line won't compile: array initializer must be an initializer list or string literal
return result;
}
private:
struct EqualToFn {
bool operator()(const T &left, const T &right) const {
return std::memcmp(&left[0],
&right[0],
sizeof(T)) == 0;
}
};
struct HashFn {
size_t operator()(const T &k) const {
return std::_Hash_impl::hash(k);
}
};
std::unordered_map<T, std::string, HashFn, EqualToFn> m_cache;
};
int main()
{
StringCache<UserKeyV1> cache1;
//uint8_t uniqueKey[16]; // this key was provided by 3rd party lib
UserKeyV1 uniqueKey;
std::string strUsername = "username";
cache1.addString(uniqueKey, strUsername);
}
I think if you want to avoid copy you can just use std::string_view class, the code becomes simpler.
#include <unordered_map>
#include <string_view>
using UserKeyV1 = std::string_view;
template <typename T>
class StringCache
{
public:
bool addString(const T &userKey, const std::string &value)
{
auto [it, result] = m_cache.try_emplace(userKey, value);
return result;
}
std::unordered_map<T, std::string> m_cache;
};
int main()
{
StringCache<UserKeyV1> cache1;
uint8_t uniqueKey[16]; // this key was provided by 3rd party lib
std::string strUsername = "username";
cache1.addString(std::string_view{(const char*)uniqueKey, sizeof(uniqueKey)}, strUsername);
}
for c++20 you can probably use std::span, or to implement your own wrapper for earlier standards.
I think the best way of doing this (without copying the value as you requested) would be to calculate an hash using the uint8_t[16] array and use the hash as key. You can choose one among the many hashing algorithms already existing (xxhash, md5, sha32, crc32 etc...).
The best match with your case would be crc32.
Any of these usually takes a const void* and a size_t to calculate the memory hash, you can pass your key address and size without copying it as you requested.
An example would be to use crc32 hash (basically an uint32_t) and to use it as key for the unordered_map.
So you can still keep your class, by adding one line in your example.
StringCache<uint32_t> cache1;
uint8_t uniqueKey[16];
cache1.addString(GetArrayHash(uniqueKey), strUsername);
You can't use this solution if you need to get back the uint8_t[16] from your cache. Based on your class code you posted there are no uses of the actual key value.
Note: an unordered_map with uint32_t as key will use an identify function as hash algorithm, in short by using crc32 hashing you are probably not losing efficiency but this is not guaranteed by the standard since it does not define exactly which algorithm to use to hash an array of uint8_t (but the msvc and gcc implementations I have been able to test use crc32).
using this approach you can definitely remove your HashFn and EqualToFn to your StringCache class.
template <typename T>
class StringCache
{
public:
bool addString(const T &userKey, const std::string &value)
{
auto [it, result] = m_cache.try_emplace(userKey, value);
}
private:
std::unordered_map<T, std::string> cache_;
}

Virtually turn vector of struct into vector of struct members

I have a function that takes a vector-like input. To simplify things, let's use this print_in_order function:
#include <iostream>
#include <vector>
template <typename vectorlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme) {
for (int i : order)
std::cout << printme[i] << std::endl;
}
int main() {
std::vector<int> printme = {100, 200, 300};
std::vector<int> order = {2,0,1};
print_in_order(order, printme);
}
Now I have a vector<Elem> and want to print a single integer member, Elem.a, for each Elem in the vector. I could do this by creating a new vector<int> (copying a for all Elems) and pass this to the print function - however, I feel like there must be a way to pass a "virtual" vector that, when operator[] is used on it, returns this only the member a. Note that I don't want to change the print_in_order function to access the member, it should remain general.
Is this possible, maybe with a lambda expression?
Full code below.
#include <iostream>
#include <vector>
struct Elem {
int a,b;
Elem(int a, int b) : a(a),b(b) {}
};
template <typename vectorlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme) {
for (int i : order)
std::cout << printme[i] << std::endl;
}
int main() {
std::vector<Elem> printme = {Elem(1,100), Elem(2,200), Elem(3,300)};
std::vector<int> order = {2,0,1};
// how to do this?
virtual_vector X(printme) // behaves like a std::vector<Elem.a>
print_in_order(order, X);
}
It's not really possible to directly do what you want. Instead you might want to take a hint from the standard algorithm library, for example std::for_each where you take an extra argument that is a function-like object that you call for each element. Then you could easily pass a lambda function that prints only the wanted element.
Perhaps something like
template<typename vectorlike, typename functionlike>
void print_in_order(std::vector<int> const & order,
vectorlike const & printme,
functionlike func) {
for (int i : order)
func(printme[i]);
}
Then call it like
print_in_order(order, printme, [](Elem const& elem) {
std::cout << elem.a;
});
Since C++ have function overloading you can still keep the old print_in_order function for plain vectors.
Using member pointers you can implement a proxy type that will allow you view a container of objects by substituting each object by one of it's members (see pointer to data member) or by one of it's getters (see pointer to member function). The first solution addresses only data members, the second accounts for both.
The container will necessarily need to know which container to use and which member to map, which will be provided at construction. The type of a pointer to member depends on the type of that member so it will have to be considered as an additional template argument.
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
private:
const Container * m_container;
MemberPtr m_member;
};
Next, implement the operator[] operator, since you mentioned that it's how you wanted to access your elements. The syntax for dereferencing a member pointer can be surprising at first.
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
// Dispatch to the right get method
auto operator[](const size_t p_index) const
{
return (*m_container)[p_index].*m_member;
}
private:
const Container * m_container;
MemberPtr m_member;
};
To use this implementation, you would write something like this :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
virtual_vector<decltype(printme), decltype(&Elem::a)> X(printme, &Elem::a);
print_in_order(order, X);
}
This is a bit cumbersome since there is no template argument deduction happening. So lets add a free function to deduce the template arguments.
template<class Container, class MemberPtr>
virtual_vector<Container, MemberPtr>
make_virtual_vector(const Container & p_container, MemberPtr p_member_ptr)
{
return{ p_container, p_member_ptr };
}
The usage becomes :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
auto X = make_virtual_vector(printme, &Elem::a);
print_in_order(order, X);
}
If you want to support member functions, it's a little bit more complicated. First, the syntax to dereference a data member pointer is slightly different from calling a function member pointer. You have to implement two versions of the operator[] and enable the correct one based on the member pointer type. Luckily the standard provides std::enable_if and std::is_member_function_pointer (both in the <type_trait> header) which allow us to do just that. The member function pointer requires you to specify the arguments to pass to the function (non in this case) and an extra set of parentheses around the expression that would evaluate to the function to call (everything before the list of arguments).
template<class Container, class MemberPtr>
class virtual_vector
{
public:
virtual_vector(const Container & p_container, MemberPtr p_member_ptr) :
m_container(&p_container),
m_member(p_member_ptr)
{}
// For mapping to a method
template<class T = MemberPtr>
auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == true, const size_t> p_index) const
{
return ((*m_container)[p_index].*m_member)();
}
// For mapping to a member
template<class T = MemberPtr>
auto operator[](std::enable_if_t<std::is_member_function_pointer<T>::value == false, const size_t> p_index) const
{
return (*m_container)[p_index].*m_member;
}
private:
const Container * m_container;
MemberPtr m_member;
};
To test this, I've added a getter to the Elem class, for illustrative purposes.
struct Elem {
int a, b;
int foo() const { return a; }
Elem(int a, int b) : a(a), b(b) {}
};
And here is how it would be used :
int main() {
std::vector<Elem> printme = { Elem(1,100), Elem(2,200), Elem(3,300) };
std::vector<int> order = { 2,0,1 };
{ // print member
auto X = make_virtual_vector(printme, &Elem::a);
print_in_order(order, X);
}
{ // print method
auto X = make_virtual_vector(printme, &Elem::foo);
print_in_order(order, X);
}
}
You've got a choice of two data structures
struct Employee
{
std::string name;
double salary;
long payrollid;
};
std::vector<Employee> employees;
Or alternatively
struct Employees
{
std::vector<std::string> names;
std::vector<double> salaries;
std::vector<long> payrollids;
};
C++ is designed with the first option as the default. Other languages such as Javascript tend to encourage the second option.
If you want to find mean salary, option 2 is more convenient. If you want to sort the employees by salary, option 1 is easier to work with.
However you can use lamdas to partially interconvert between the two. The lambda is a trivial little function which takes an Employee and returns a salary for him - so effectively providing a flat vector of doubles we can take the mean of - or takes an index and an Employees and returns an employee, doing a little bit of trivial data reformatting.
template<class F>
struct index_fake_t{
F f;
decltype(auto) operator[](std::size_t i)const{
return f(i);
}
};
template<class F>
index_fake_t<F> index_fake( F f ){
return{std::move(f)};
}
template<class F>
auto reindexer(F f){
return [f=std::move(f)](auto&& v)mutable{
return index_fake([f=std::move(f),&v](auto i)->decltype(auto){
return v[f(i)];
});
};
}
template<class F>
auto indexer_mapper(F f){
return [f=std::move(f)](auto&& v)mutable{
return index_fake([f=std::move(f),&v](auto i)->decltype(auto){
return f(v[i]);
});
};
}
Now, print in order can be rewritten as:
template <typename vectorlike>
void print(vectorlike const & printme) {
for (auto&& x:printme)
std::cout << x << std::endl;
}
template <typename vectorlike>
void print_in_order(std::vector<int> const& reorder, vectorlike const & printme) {
print(reindexer([&](auto i){return reorder[i];})(printme));
}
and printing .a as:
print_in_order( reorder, indexer_mapper([](auto&&x){return x.a;})(printme) );
there may be some typos.

boost variant with custom classes

I was trying boost-variant with custom classes. I understood that a safe way to access the content of a class is using boost::static_visitor. Do you know why the code below doesn't compile? Are there any requirement on the signature/declaration of boost::static_visitor in order to be used?
I found this question Why can't I visit this custom type with boost::variant? but I didn't get it.
Regards
AFG
#include <iostream>
#include <algorithm>
#include <boost/variant.hpp>
struct CA{};
struct ca_visitor : public boost::static_visitor<CA>
{
const CA& operator()(const CA& obj ) const { return obj;}
};
struct CB{};
struct cb_visitor : public boost::static_visitor<CB>
{
const CB& operator()(const CB& obj) const { return obj;}
};
int main(){
typedef boost::variant<
CA
,CB > v_type;
v_type v;
const CA& a = boost::apply_visitor( ca_visitor(), v );
}
First of all, the template argument of boost::static_visitor<> should specify the type returned by the call operator. In your case, ca_visitor's call operator returns a CA const&, not a CA.
But that is not the biggest issue. The biggest issue is that you seem to have a misconception of how variant<> and static_visitor<> should work.
The idea of a boost::variant<> is that it can hold values of any of the types you specify in the template argument list. You don't know what that type is, and therefore you provide a visitor with several overloaded call operators for handling each possible case.
Therefore, when you provide a visitor, you need to make sure it has all necessary overloads of operator() that accept the types your variant can hold. If you fail to do so, Boost.Variant causes a compilation error to be generated (and is doing you a favor, because you forgot to handle some cases).
This is the issue you are facing: your visitor does not have a call operator accepting an object of type CB.
This is an example of a correct use of boost::variant<> and static_visitor<>:
#include <iostream>
#include <algorithm>
#include <boost/variant.hpp>
struct A{};
struct B{};
struct my_visitor : public boost::static_visitor<bool>
// ^^^^
// This must be the same as the
// return type of your call
// operators
{
bool operator() (const A& obj ) const { return true; }
bool operator() (const B& obj) const { return false; }
};
int main()
{
A a;
B b;
my_visitor mv;
typedef boost::variant<A, B> v_type;
v_type v = a;
bool res = v.apply_visitor(mv);
std::cout << res; // Should print 1
v = b;
res = v.apply_visitor(mv);
std::cout << res; // Should print 0
}

Overloaded [] operator on template class in C++ with const / nonconst versions

Whew, that was a long title.
Here's my problem. I've got a template class in C++ and I'm overloading the [] operator. I have both a const and a non-const version, with the non-const version returning by reference so that items in the class can be changed as so:
myobject[1] = myvalue;
This all works until I use a boolean as the template parameter. Here's a full example that shows the error:
#include <string>
#include <vector>
using namespace std;
template <class T>
class MyClass
{
private:
vector<T> _items;
public:
void add(T item)
{
_items.push_back(item);
}
const T operator[](int idx) const
{
return _items[idx];
}
T& operator[](int idx)
{
return _items[idx];
}
};
int main(int argc, char** argv)
{
MyClass<string> Test1; // Works
Test1.add("hi");
Test1.add("how are");
Test1[1] = "you?";
MyClass<int> Test2; // Also works
Test2.add(1);
Test2.add(2);
Test2[1] = 3;
MyClass<bool> Test3; // Works up until...
Test3.add(true);
Test3.add(true);
Test3[1] = false; // ...this point. :(
return 0;
}
The error is a compiler error and the message is:
error: invalid initialization of non-const reference of type ‘bool&’ from a temporary of type ‘std::_Bit_reference’
I've read up and found that STL uses some temporary data types, but I don't understand why it works with everything except a bool.
Any help on this would be appreciated.
Because vector<bool> is specialized in STL, and does not actually meet the requirements of a standard container.
Herb Sutter talks about it more in a GOTW article: http://www.gotw.ca/gotw/050.htm
A vector<bool> is not a real container. Your code is effectively trying to return a reference to a single bit, which is not allowed. If you change your container to a deque, I believe you'll get the behavior you expect.
A vector<bool> is not implemented like all other vectors, and does not work like them either. You are better off simply not using it, and not worrying if your code can't handle its many peculiarities - it is mostly considered to be A Bad Thing, foisted on us by some unthinking C++ Standard committee members.
Some monor changes to your class should fix it.
template <class T>
class MyClass
{
private:
vector<T> _items;
public:
// This works better if you pass by const reference.
// This allows the compiler to form temorary objects and pass them to the method.
void add(T const& item)
{
_items.push_back(item);
}
// For the const version of operator[] you were returning by value.
// Normally I would have returned by const ref.
// In normal situations the result of operator[] is T& or T const&
// But in the case of vector<bool> it is special
// (because apparently we want to pack a bool vector)
// But technically the return type from vector is `reference` (not T&)
// so it you use that it should compensate for the odd behavior of vector<bool>
// Of course const version is `const_reference`
typename vector<T>::const_reference operator[](int idx) const
{
return _items[idx];
}
typename vector<T>::reference operator[](int idx)
{
return _items[idx];
}
};
As the other answers point out, a specialization is provided to optimize for space allocation in the case of vector< bool>.
However you can still make your code valid if you make use of vector::reference instead of T&. In fact it is a good practice to use container::reference when referencing data held by a STL container.
T& operator[](int idx)
becomes
typename vector<T>::reference operator[](int idx)
Of course ther is also a typedef for const reference:
const T operator[](int idx) const
and this one becomes (removing the useless extra copy)
typename vector<T>::const_reference operator[](int idx) const
The reason for the error is that vector<bool> is specialized to pack the boolean values stored within and vector<bool>::operator[] returns some sort of proxy that lets you access the value.
I don't think a solution would be to return the same type as vector<bool>::operator[] because then you'd be just copying over the regrettable special behavior to your container.
If you want to keep using vector as the underlying type, I believe the bool problem could be patched up by using a vector<MyBool> instead when MyClass is instantiated with bool.
It might look like this:
#include <string>
#include <vector>
using namespace std;
namespace detail
{
struct FixForBool
{
bool value;
FixForBool(bool b): value(b) {}
operator bool&() { return value; }
operator const bool& () const { return value; }
};
template <class T>
struct FixForValueTypeSelection
{
typedef T type;
};
template <>
struct FixForValueTypeSelection<bool>
{
typedef FixForBool type;
};
}
template <class T>
class MyClass
{
private:
vector<typename detail::FixForValueTypeSelection<T>::type> _items;
public:
void add(T item)
{
_items.push_back(item);
}
const T operator[](int idx) const
{
return _items[idx];
}
T& operator[](int idx)
{
return _items[idx];
}
};
int main(int argc, char** argv)
{
MyClass<string> Test1; // Works
Test1.add("hi");
Test1.add("how are");
Test1[1] = "you?";
MyClass<int> Test2; // Also works
Test2.add(1);
Test2.add(2);
Test2[1] = 3;
MyClass<bool> Test3; // Works up until...
Test3.add(true);
Test3.add(true);
Test3[1] = false; // ...this point. :(
return 0;
}

How to implement sorting method for a c++ priority_queue with pointers

My priority queue declared as:
std::priority_queue<*MyClass> queue;
class MyClass {
bool operator<( const MyClass* m ) const;
}
is not sorting the items in the queue.
What is wrong? I would not like to implement a different (Compare) class.
Answer summary:
The problem is, the pointer addresses are sorted. The only way to avoid this is a class that 'compares the pointers'.
Now implemented as:
std::priority_queue<*MyClass, vector<*MyClass>, MyClass::CompStr > queue;
class MyClass {
struct CompStr {
bool operator()(MyClass* m1, MyClass* m2);
}
}
Give the que the Compare functor ptr_less.
If you want the ptr_less to be compatible with the rest of the std library (binders, composers, ... ):
template<class T>
struct ptr_less
: public binary_function<T, T, bool> {
bool operator()(const T& left, const T& right) const{
return ((*left) <( *right));
}
};
std::priority_queue<MyClass*, vector<MyClass*>, ptr_less<MyClass*> > que;
Otherwise you can get away with the simplified version:
struct ptr_less {
template<class T>
bool operator()(const T& left, const T& right) const {
return ((*left) <( *right));
}
};
std::priority_queue<MyClass*, vector<MyClass*>, ptr_less > que;
The operator <() you have provided will compare a MyClass object with a pointer to a MyClass object. But your queue contains only pointers (I think). You need a comparison function that takes two pointers as parameters.
All this is based on some suppositions - please post your actual code, using copy and paste.
Since your priority_queue contains only pointer values, it will use the default comparison operator for the pointers - this will sort them by address which is obviously not what you want. If you change the priority_queue to store the class instances by value, it will use the operator you defined. Or, you will have to provide a comparison function.
Not sure about the priority queue stuff because I've never used it but to do a straight sort, you can do this:
class A
{
friend struct ComparePtrToA;
public:
A( int v=0 ):a(v){}
private:
int a;
};
struct ComparePtrToA
{
bool operator()(A* a1, A* a2) {return a1->a < a2->a;}
};
#include <vector>
#include <algorithm>
int _tmain(int argc, _TCHAR* argv[])
{
vector<A*> someAs;
someAs.push_back(new A(1));
someAs.push_back(new A(3));
someAs.push_back(new A(2));
sort( someAs.begin(), someAs.end(), ComparePtrToA() );
}
Note the memory leaks, this is only an example...
Further note: This is not intended to be an implementation of priority queue! The vector is simply an example of using the functor I created to compare two objects via their pointers. Although I'm aware of what a priority queue is and roughly how it works, I have never used the STL features that implement them.
Update: I think TimW makes some valid points. I don't know why he was downvoted so much. I think my answer can be improved as follows:
class A
{
public:
A( int v=0 ):a(v){}
bool operator<( const A& rhs ) { return a < rhs.a; }
private:
int a;
};
struct ComparePtrToA
{
bool operator()(A* a1, A* a2) {return *a1 < *a2;}
};
which is cleaner (especially if you consider having a container of values rather than pointers - no further work would be necessary).