Custom sort and key uniqueness in std::map C++ - sorting

Consider the follow std::map code.
The idea is to sort keys based only a double value even though the key contains more information.
In the custom sort used for the map, there are 3 test conditions the first of which does not work as expected.
I would like to understand why before I assume the next two actually behave as I expect them too.
Test choices 2 and 3 explicit that I do not care about the content of s_Rule (some pointer containing information) for sorting purposes but how does that also break the key mechanism of map.
In fact, 2 and 3 are literally the same since if double == double and ptr == ptr than the key is obviously the same.
This may (or may not) be related to question :
How can I declare a custom sort function on std::map declaration?
in which Simon9987 asks at the end why sort has broken the std::find function
class s_Rule
{
int x;
s_Rule() : x(0) {} // irrelevant content for question
}
typedef std::pair<double, s_Rule*> ruleSavedData;
struct sortByDouble
{
bool operator()(const ruleSavedData& a, const ruleSavedData& b) const
{
return a.first < b.first; // // not ok
// return a.first < b.first || (a.first == b.first && a.second != b.second); // ok
// return a.first <= b.first; // ok
}
};
typedef std::map<ruleSavedData, std::tuple<int, int, int>, sortByDouble> t_splitRulePool;
t_splitRulePool _splitRulePool;
auto test1 = new s_Rule();
auto test2 = new s_Rule();
auto test3 = new s_Rule();
const auto pp1 = ruleSavedData(687.00, test1);
_splitRulePool[pp1] = std::tuple<int,int,int>({1, 0, 0});
for (const auto& sr : _splitRulePool)
std::cout << sr.first.first << " with rule: " << sr.first.second << " and value " << std::get<0>(sr.second) << std::endl;
// Output: 687 with rule: 000001F9B60509E0 and value 1
const auto pp2 = ruleSavedData(688.00, test2);
_splitRulePool[pp2] = std::tuple<int, int, int>({ 2, 0, 0 });
for (const auto& sr : _splitRulePool)
std::cout << sr.first.first << " with rule: " << sr.first.second << " and value " << std::get<0>(sr.second) << std::endl;
// Output:
// 687 with rule: 000001F9B60509E0 and value 1
// 688 with rule: 000001F9B60506E0 and value 2
const auto pp3 = ruleSavedData(687.00, test3);
_splitRulePool[pp3] = std::tuple<int, int, int>({ 3, 0, 0 });
for (const auto& sr : _splitRulePool)
std::cout << sr.first.first << " with rule: " << sr.first.second << " and value " << std::get<0>(sr.second) << std::endl;
// Output:
// 687 with rule: 000001F9B60509E0 and value 3 -> key of pp1 gets overwritten with sortByDouble choice 1 but not with the others
// 688 with rule: 000001F9B60506E0 and value 2
// "Expected" output:
// 687 with rule: 000001F9B60509E0 and value 1
// 687 with rule: (&pp3) and value 3 // comes after simply because first sort < key is not true
// 688 with rule: 000001F9B60506E0 and value 2
delete test1;
delete test2;
delete test3;

Related

c++11 how to check template parameter pack has N args before calling function with N args

Following on from this extracting a template parameter pack with different types into a vector of doubles produces warnings and cigien's answer.
I have the following code:
enum class p_type {p1, p2, p3};
class testerx
{
public:
void process1(double a)
{
std::cout << "1" << a << std::endl;
};
void process2(double a, double b)
{
std::cout << "2" << a << " " << b << std::endl;
};
void process3(double a, double b, double c)
{
std::cout << "3" << a << " " << b << " " << c << std::endl;
};
};
// The template type
template<typename TESTER, typename... ARGS>
class tester_templatex
{
public:
explicit tester_templatex(p_type type) : m_type(type) {};
void process(ARGS... args)
{
// Create a vector to put the args into. use double since that can hold all of the types
// that I am using
size_t param_count = sizeof...(args);
std::cout << "PARAM COUNT X " << param_count << std::endl;
std::vector<double> args_vect = {static_cast<double>(args)...};
for (auto arg : args_vect)
{
std::cout << "arg: " << arg << std::endl;
}
// Now call the tester
std::cout << "running tester: ";
switch (m_type)
{
case p_type::p1:
if constexpr (sizeof...(args) == 1)
m_tester.process1(args...);
break;
case p_type::p2:
if constexpr (sizeof...(args) == 2)
m_tester.process2(args...);
break;
case p_type::p3:
if constexpr (sizeof...(args) == 3)
m_tester.process3(args...);
break;
}
std::cout << std::endl;
};
p_type m_type;
TESTER m_tester;
};
main:
int main() {
tester_templatex<testerx, int> templatex1(p_type::p1);
tester_templatex<testerx, int, double> templatex2(p_type::p2);
tester_templatex<testerx, int, double, int> templatex3(p_type::p3);
templatex1.process(4);
templatex2.process(4, 5.123);
templatex3.process(4, 5.123, 6);
return 0;
}
Here I have test class with 3 different functions. I have a template class which picks the function to call based on the p_type (bad name - dont ask!).
This works for c++17 compiled code. But I only have c++11 where I need to run this code. c++11 does not support if constexpr:
case p_type::p3:
if constexpr (sizeof...(args) == 3)
m_tester.process3(args...);
break;
Without the if constexpr I get errors that the m_tester.process1/2/3 functions that don't match the parameter pack because they don't have the right number of parameters.
How can I fix this for c++11? - is it possible with a similar method?
Is there another way to extract N arguments from a parameter pack in c++11? - or some sort of type traits check?
For each of your functions have an overload that does nothing:
template<typename... ARGS>
void process3(ARGS&...) { }
and then just call the function without testing for the size of the pack:
case p_type::p3:
m_tester.process3(args...);
break;
This should pick the non-templated function when there are suitably many arguments, and the function template in other cases.

What does String do that I'm not doing? c++11

I am still new to c++, so bear with me.
I was trying to learn more about how std::move works and I saw an example where they used std::move to move the string to a different function and then showed using std::cout that no string remained. I thought cool, let's see if I can make my own class and do the same:
#include <iostream>
#include <string>
class integer
{
private:
int *m_i;
public:
integer(int i=0) : m_i(new int{i})
{
std::cout << "Calling Constructor\n";
}
~integer()
{
if(m_i != nullptr) {
std::cout << "Deleting integer\n";
delete m_i;
m_i = nullptr;
}
}
integer(integer&& i) : m_i(nullptr) // move constructor
{
std::cout << "Move Constructor\n";
m_i = i.m_i;
i.m_i = nullptr;
}
integer(const integer& i) : m_i(new int) { // copy constructor
std::cout << "Copy Constructor\n";
*m_i = *(i.m_i);
}
//*
integer& operator=(integer&& i) { // move assignment
std::cout << "Move Assignment\n";
if(&i != this) {
delete m_i;
m_i = i.m_i;
i.m_i = nullptr;
}
return *this;
}
integer& operator=(const integer &i) { // copy assignment
std::cout << "Copy Assignment\n";
if(&i != this) {
m_i = new int;
*m_i = *(i.m_i);
}
return *this;
}
int& operator*() const { return *m_i; }
int* operator->() const { return m_i; }
bool empty() const noexcept {
if(m_i == nullptr) return true;
return false;
}
friend std::ostream& operator<<(std::ostream &out, const integer i) {
if(i.empty()) {
std::cout << "During overload, i is empty\n";
return out;
}
out << *(i.m_i);
return out;
}
};
void g(integer i) { std::cout << "G-wiz - "; std::cout << "The g value is " << i << '\n'; }
void g(std::string s) { std::cout << "The g value is " << s << '\n'; }
int main()
{
std::string s("Hello");
std::cout << "Now for string\n";
g(std::move(s));
if(s.empty()) std::cout << "s is empty\n";
g(s);
std::cout << "\nNow for integer\n";
integer i = 77;
if(!i.empty()) std::cout << "i is " << i << '\n';
else std::cout << "i is empty\n";
g(i);
std::cout << "Move it\n";
g(std::move(i)); // rvalue ref called
if(!i.empty()) std::cout << "i is " << i << '\n';
else std::cout << "i is empty\n";
g(i);
return 0;
}
And this is my output:
Now for string
The g value is Hello
s is empty
The g value is
Now for integer
Calling Constructor
Copy Constructor
i is 77
Deleting integer
Copy Constructor
G-wiz - Copy Constructor
The g value is 77
Deleting integer
Deleting integer
Move it
Move Constructor
G-wiz - Copy Constructor
The g value is 77
Deleting integer
Deleting integer
i is empty
Copy Constructor
Process returned 255 (0xFF) execution time : 7.633 s
Press any key to continue.
As you can see, it crashes when it enters g the second time, never even getting to the operator<<() function. How is it that the empty std::string s can be passed to g where my empty integer i crashes the program?
Edit: Fixed new int vs. new int[] error. Thanks n.m.
Your "empty integer" crashes the program because it contains a null pointer. You are trying to dereference it when you use it at the right hand side of the assignment.
An empty string is a normal usable string. There are no unchecked null pointer dereferences in the std::string code.
You have to ensure that the empty state of your object is a usable one. Start with defining a default constructor. Does it make sense for your class? If not, then move semantic probably doesn't either. If yes, a moved-from object in the move constructor should probably end up in the same state as a default-constructed object. A move assignment can act as a swap operation, so there the right-hand-side may end up either empty or not.
If you don't want to define a usable empty state for your class, and still want move semantics, you simply cannot use an object after it has been moved from. You still need to make sure that an empty object is destructible.

use of "&" with auto [duplicate]

This question already has answers here:
Range based loop: get item by value or reference to const?
(5 answers)
Closed 6 years ago.
When we need to use " &" and when not to?
for example in below, both for loops produce identical result.
std::vector< Product* > itemByColor = pF.by_color( vecProds, Color::Red );
for( auto i : itemByColor )
{
std::cout << " product name <<" << i->name<< std::endl;
}
AND
for( auto& i : itemByColor )
{
std::cout << " product name <<" << i->name<< std::endl;
}
More or less the same as whether you would decide to type std::string or (const) std::string&. That is, whether you want to copy the object or to take a reference to it.
std::vector<int> my_vector{ 1, 2, 3, 4, 5 };
int copy = my_vector[ 0 ];
int& reference = my_vector[ 0 ];
++copy;
std::cerr << my_vector[ 0 ] << '\n'; // Outputs '1', since the copy was incremented, not the original object itself
++reference;
std::cerr << my_vector[ 0 ] << '\n'; // Outputs '2', since a reference to the original object was incremented
// For each 'n' in 'my_vector', taken as a copy
for( auto n : my_vector )
{
// The copy ('n') is modified, but the original remains unaffected
n = 123;
}
// For each 'n' in 'my_vector', taken as a reference
for( auto& n : my_vector )
{
// The original is incremented by 42, since 'n' is a reference to it
n += 42;
}
// At this point, 'my_vector' contains '{ 44, 44, 45, 46, 47 }'

How can I add a reference to a value-type in vala

In c++ I can add a reference to a value type, for example :
int a = 12;
int &b = a;
a--;
cout << "a = " << a << ", b = " << b << endl;
Will give :
a = 11, b = 11
Is there a way to do the same in vala without using pointers ?
Is there a way to do the same in vala
Yes.
without using pointers ?
No.
If, however, you are passing them to a function, you can use a ref parameter:
void decrement (ref value) {
value--;
}
void do_stuff () {
int a = 12;
decrement (ref a);
assert (a == 11);
}

Combine three 32-bit identifiers into one 32-bit identifier?

Given three identifiers, combine them into a single 32-bit value.
It is known, that the first identifier may have (2^8)-1 different values. Analogically, the second (2^8)-1 and the third (2^10)-1. Therefore the total count of identifiers of all kinds will not exceed (2^32)-1.
Example solution could be to have a map:
key: 32 bits,
value: 8 (or 10) bits.
The value would begin at 0 and be incremented every time a new identifier is provided.
Can it be done better? (instead of 3 maps) Do you see a problem with this solution?
To clarify, the identifier can hold ANY values from the range <0, 2^32). The only information that is given, is that the total number of them will not exceed (2^8)-1 (or 10th).
The identifiers can have the same values (it's completely random). Consider the randomness source memory addresses given by the OS to heap-allocated memory (e.g. using a pointer as an identifier). I realize this might work differently on x64 systems, however, I hope the general's problem solution to be similiar to this specific one.
This means that a simple bit shifting is out of question.
You could try something like this:-
#include <map>
#include <iostream>
class CombinedIdentifier
{
public:
CombinedIdentifier (unsigned id1, unsigned id2, unsigned id3)
{
m_id [0] = id1;
m_id [1] = id2;
m_id [2] = id3;
}
// version to throw exception on ID not found
static CombinedIdentifier GetIdentifier (unsigned int id)
{
// search m_store for a value = id
// if found, get key and return it
// else....throw an exception->id not found
}
// version to return found/not found instead of throwing an exception
static bool GetIdentifier (unsigned int id, CombinedIdentifier &out)
{
// search m_store for a value = id
// if found, get key and save it to 'out' and return true
// else....return false
}
int operator [] (int index) { return m_id [index]; }
bool operator < (const CombinedIdentifier &rhs) const
{
return m_id [0] < rhs.m_id [0] ? true :
m_id [1] < rhs.m_id [1] ? true :
m_id [2] < rhs.m_id [2];
}
bool operator == (const CombinedIdentifier &rhs) const
{
return m_id [0] == rhs.m_id [0] &&
m_id [1] == rhs.m_id [1] &&
m_id [2] == rhs.m_id [2];
}
bool operator != (const CombinedIdentifier &rhs) const
{
return !operator == (rhs);
}
int GetID ()
{
int
id;
std::map <CombinedIdentifier, int>::iterator
item = m_store.find (*this);
if (item == m_store.end ())
{
id = m_store.size () + 1;
m_store [*this] = id;
}
else
{
id = item->second;
}
return id;
}
private:
int
m_id [3];
static std::map <CombinedIdentifier, int>
m_store;
};
std::map <CombinedIdentifier, int>
CombinedIdentifier::m_store;
int main ()
{
CombinedIdentifier
id1 (2, 4, 10),
id2 (9, 14, 1230),
id3 (4, 1, 14560),
id4 (9, 14, 1230);
std::cout << "id1 = " << id1.GetID () << std::endl;
std::cout << "id2 = " << id2.GetID () << std::endl;
std::cout << "id3 = " << id3.GetID () << std::endl;
std::cout << "id4 = " << id4.GetID () << std::endl;
}
You can get this with bit shifting and unsafe code.
There is an article on SO: What are bitwise shift (bit-shift) operators and how do they work?
Then you can use the whole 32bit range for your three values
---- 8 bits ---- | ---- 8 bits ---- | ---- 10 bits ---- | ---- unused 6 bits ----
int result = firstValue << (8 + 10 + 6);
result += secondValue << (10 + 6);
result += thirdValue << 6;
I think you could make use of a Perfect Hash Function. In particular, the link provided in that that article to Pearson Hashing seems to be appropriate. You might even be able to cut-and-paste the included C program the 2nd article except for the fact that its output is a 64-bit number not a 32-bit one. But if you modify it slightly from
for (j=0; j<8; j++) {
// standard Pearson hash (output is h)
to
for (j=0; j<4; j++) {
// standard Pearson hash (output is h)
You'll have what you need.

Resources