About modifying the parameter of a functor - c++

I have the following comparator for string objects
struct Comparator{
int x;
bool operator() (string a, string b) {
int i = 1;
if(a < b) {
i = -1;
}
i*= x;
if(i==-1) {
return true;
}
return false;
}
};
As you can see, it has a parameter x. when it is = 1 the comparison of strings is normal and when it is =-1 the comparison is inverted.
When using it in a method like sort for vector elements I can give the object instance of this comparator with the right x, but when I want to give this comparator to a template class like set, I need to give a class and not an object instance. So the only solution I see is to make x static. Is there a better solution?
Here is the example of a main where I would like to use this comparator:
int main(int argc, char** argv)
{
vector<string> vec;
vec.push_back("c");
vec.push_back("a");
vec.push_back("b");
Comparator comp;
comp.x = 1; // for normal comparison
comp.x = -1; // for inverse comparison
sort(vec.begin(),vec.end(), comp); // here I can give the functor instance
for(vector<string>::iterator it = vec.begin() ; it != vec.end(); it++)
{
cout << *it << endl;
}
set<string, Comparator> ss; // but here I must give the name of the functor class
ss.insert("c");
ss.insert("a");
ss.insert("b");
for(set<string>::iterator it = ss.begin() ; it != ss.end(); it++)
{
cout << *it << endl;
}
return 0;
}

All the relevant constructors of set also take an instance of Comp class.
set<string, Comparator> ss(Comparator(-1));
Now you only need a constructor for Comparator that initializes its member x with an appropriate value.
That said, the standard library already comes with a comparator class for this purpose:
set<string, std::greater<std::string> > ss;

That will not work: a < b does not(!) mean b < a.
You might utilize std::string::compare:
bool operator() (const std::string& a, const std::string& b) {
int result = a.compare(b) * x;
return result < 0;
}

Related

A safer way to reference a private member variable in public member function call? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I want to use a single function to interact with multiple private member variables. I've come up with:
class Some_Vectors {
public:
int is_value_in_vector(string vector_name, int value);
void append_value_to_vector(string vector_name, int value)
private:
vector<int> A;
vector<int> B;
};
// returns value's index if it's there, -1 if not
int Some_Vectors::is_value_in_vector(string vector_name, int value) {
vector<int> *selected_vector;
if (vector_name == "A") {selected_vector = &A;}
else if (vector_name == "B") {selected_vector = &B;}
for (int i = 0; i < selected_vector->size(); i++){
if (selected_vector[0][i] == value){
return i;
}
}
return -1;
}
It works, but I feels unsafe/brittle to compare strings like that. Is there a way to specifically reference a private variable in the function call?
Edited to be a (hopefully) less subjective ask. I ended up using RichardCritten's suggestion of multiple public functions that call a single private function.
You can use the unordered_map to achieve your requirements as below.
Declare the unordered_map as below.
unordered_map<string, vector<int>> umap;
Insert the values to map by using [] operator.
umap["A"] = {10,20};
umap["B"] = {30,40};
Search the key value in the unordered_map as below using find function.
string vector_name = "A";
vector_name = "A";
auto it = umap.find(vector_name);
if (it == umap.end())
return -1;
Once you find the key,value pair in the map search the particular int in the vector as below.
std::vector<int>::iterator iter = std::find(it->second.begin(), it->second.end(), 20);
if iter is not pointing the vector end then return the exact position of the int in the vector as below.
if ( iter != it->second.end())
return std::distance(it->second.begin(),iter);
else
return -1;
Your complete sample code may look like below.
int main()
{
unordered_map<string, vector<int>> umap;
// inserting values by using [] operator
umap["A"] = {10,20};
umap["B"] = {30,40};
string vector_name = "A";
vector_name = "A";
auto it = umap.find(vector_name);
if (it == umap.end())
return -1;
std::vector<int>::iterator iter = std::find(it->second.begin(), it->second.end(), 20);
if ( iter != it->second.end())
return std::distance(it->second.begin(),iter);
else
return -1;
}
I have to disagree with the other answers that suggest maps or any kind of solutions involving strings.
Using strings to identify things in code is very fragile. Some major disadvantages are: no autocomplete, no compile-time checks. There are situations where you don't have a better alternative (e.g. you don't know the identifiers at compile time), but this is not one of them.
One solution is to give meaningful names to the functions. Since you provided a toy example I will use A and B but in real life they should be meaningful names:
class X
{
public:
auto foo_a(int value) { return foo(A, value); }
auto foo_b(int value) { return foo(B, value); }
private:
int foo(std::vector<int>& v, int value) { return 24; }
std::vector<int> A;
std::vector<int> B;
};
If you want one function with a parameter to select the vector, you should select the vector with an enum. This way you have autocomplete and compile-time safety (you can't pass an invalid selector - like you could with a string - unless you bend backwards):
class Y
{
public:
enum class Selector { A, B };
auto foo(Selector selector, int value) { return foo(getVector(selector), value); }
private:
std::vector<int>& getVector(Selector selector)
{
switch (selector)
{
case Selector::A:
return A;
case Selector::B:
return B;
}
}
int foo(std::vector<int>& v, int value) { return 24; }
std::vector<int> A;
std::vector<int> B;
};
Y y{};
y.foo(Y::Selector::A, 11);
y.foo(Y::Selector::B, 1024);
First of all, if you have access to C++17 or later versions of compilers the most modern and preferable way of optional return would be using std::optional.
Regarding your question, as #Dai mentioned in the comments, the best way would be(IMHO also) to use
std::unordered_map<std::string, std::vector</*type*/>>
as the member variable and you can do as follows. See Live here
#include <vector>
#include <string>
#include <unordered_map>
#include <iostream>
using uMapType = std::unordered_map<std::string, std::vector<int>>;
class MyClass
{
private:
uMapType _vecMap;
public:
explicit MyClass(const uMapType& vecMap): _vecMap(std::move(vecMap)) {}
int getValue(const std::string &vector_name, const int value)const
{
// std::unordered_map::find the key(vector name)
auto getVec = _vecMap.find(vector_name);
if(getVec != _vecMap.cend()) // if iterator not pointing to map's end
{
const std::vector<int> &selected_vector = getVec->second;
for (std::size_t i = 0; i < selected_vector.size(); ++i)
if (selected_vector[i] == value)
return i;
}
return -1;
}
};
int main()
{
MyClass obj(
{
{"A", {1, 2, 3, 4, 5}},
{"B", {1, 2, 3, 4, 5}}
});
std::cout << obj.getValue("A", 3) << std::endl; // output: 2
std::cout << obj.getValue("B", 5) << std::endl; // output: 5
std::cout << obj.getValue("C", 3) << std::endl; // output: -1
std::cout << obj.getValue("A", 0) << std::endl; // output: -1
return 0;
}
The std::optional sample solution will look like this.
#include <optional>
using uMapType = std::unordered_map<std::string, std::vector<int>>;
class MyClass
{
private:
uMapType _vecMap;
public:
explicit MyClass(const uMapType& vecMap): _vecMap(std::move(vecMap)) {}
std::optional<int> getValue(const std::string &vector_name, const int value)const
{
if(auto getVec = _vecMap.find(vector_name); getVec != _vecMap.cend())
{
for (std::size_t i = 0; i < getVec->second.size(); ++i)
if (getVec->second[i] == value)
return i;
}
return std::nullopt;
}
};

C++ List showing last 3 items from table

Hey i have a table of teams with the names and the points they have and i'm trying to figure out how to display the last 3 teams with the least amount of points in the table?
It displays all the teams and i want it to display only the last 3 in the table but don't know what way to go about it.
These are my Accessors
string GetName
int GetPoints
int lowest = 1000;
for (int i = 0; i < numTeams; i++)
{
if (league[i].GetPoints() < lowest)
{
lowest = league[i].GetPoints();
}
}
for (int i = 0; i < numTeams; i++)
{
if (league[i].GetPoints() == lowest)
{
cout << "\tThe lowest goals against is: " << league[i].GetName() << endl;
}
}
Actually, you don't need variable lowest, if you would sort the data before printing.
#include <algorithm>
// Sort using a Lambda expression.
std::sort(std::begin(league), std::end(league), [](const League &a, const League &b) {
return a.GetPoints() < b.GetPoints();
});
int last = 3;
for (int i = 0; i < last; i++)
{
cout << "\tThe lowest goals against is: " << league[i].GetName() << endl;
}
U could probably start by sorting your array
#include <algorithm>
std::array<int> foo;
std::sort(foo.begin(), foo.end());
and then Iterate From Your Last Element to your Last - 3. (U can use Reverse Iterators)
for (std::vector<int>::reverse_iterator it = v.rend() ; it != v.rend() + 3;
it++) {
//Do something
}
or by using auto
for (auto it = v.rend() ; it != v.rend() + 3; ++it) {
//Do something
}
In my example I've created test class(TestTeam) to implement several important methods for objects in your task.
I use std::sort method to sort container of objects, by default std::sort compares objects by less(<) operation, so I have overrided operator < for TestTeam object
bool operator < ( const TestTeam& r) const
{
return GetPoints() < r.GetPoints();
}
Also we could pass as third parameter another compare method or lambda method as shown in below answers:
std::sort(VecTeam.begin(), VecTeam.end(), [](const TestTeam& l, const TestTeam& r)
{
return l.GetPoints() < r.GetPoints();
});
And example when we use global method to compare:
bool CompareTestTeamLess(const TestTeam& l, const TestTeam& r)
{
return l.GetPoints() < r.GetPoints();
}
//...
// some code
//...
// In main() we use global method to sort
std::sort(VecTeam.begin(), VecTeam.end(), ::CompareTestTeamLess);
You can try my code with vector as container:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
// Test class for example
class TestTeam
{
public:
TestTeam(int16_t p, const std::string& name = "Empty name"):mPoints(p), mName(name)
{
};
int16_t GetPoints() const {return mPoints;}
const std::string& GetName() const {return mName;}
void SetName( const std::string& name ) {mName=name;}
bool operator < ( const TestTeam& r) const
{
return GetPoints() < r.GetPoints();
}
private:
int16_t mPoints;
std::string mName;
};
int main(int argc, const char * argv[])
{
const uint32_t COUNT_LOWEST_ELEMENTS_TO_FIND = 3;
// Fill container by test data with a help of non-explicit constructor to solve your task
std::vector<TestTeam> VecTeam {3,5,8,9,11,2,14,7};
// Here you can do others manipulations with team data ...
//Sort vector by GetPoints overloaded in less operator. After sort first three elements will be with lowest points in container
std::sort(VecTeam.begin(), VecTeam.end());
//Print results as points - name
std::for_each( VecTeam.begin(), VecTeam.begin() + COUNT_LOWEST_ELEMENTS_TO_FIND, [] (TestTeam el)
{
std::cout << el.GetPoints() << " - " << el.GetName() << std::endl;
} );
}
I made test class TestTeam only to implement test logic for your object.
If you try launch the program you can get next results:
2 - Empty name
3 - Empty name
5 - Empty name
Program ended with exit code: 0

Comparing function of binary_search

I am trying to run binary_search on vector of custom objects.
struct T{
string name;
T(string n):name(n){};
bool operator < ( T * n ) const {
return name < n -> name;
}
bool operator == ( T * n ) const {
return name == n -> name;
}
};
vector<T *> t;
t.push_back(new T("one"));
t.push_back(new T("two"));
t.push_back(new T("three"));
bool has_3 = binary_search( t.begin(), t.end(), new T("two") ) ;
if( has_3 ){
cout <<"Its there" << endl;
}
The comparation function should be just fine yet when i run the code has_3 equals to 0 = the element isnt present in vector. Is this problem caused by my overloading of < ? I see no reason why this shouldnt find the value. Considering the order of insertion into vector it should be sorted
Thanks for help.
There are several reasons why this shouldn't find the value:
The range must be sorted; your range is out of alphabetical order
Your comparison functionality is defined between T and T*, while you search a vector of T* for a T*.
You can fix the first problem by swapping "two" and "three", and the second problem by making a vector of T:
struct T{
string name;
T(string n):name(n){};
bool operator < ( const T &n ) const {
return name < n.name;
}
// operator == is not necessary for binary_search
};
int main() {
vector<T> t;
t.push_back(T("one"));
t.push_back(T("three"));
t.push_back(T("two"));
bool has_3 = binary_search( t.begin(), t.end(), T("two") ) ;
if( has_3 ){
cout <<"Its there" << endl;
}
return 0;
}
Demo 1.
If you do have no way but to construct a vector of pointers, you have this ugly work-around available (I strongly recommend against it):
struct T{
string name;
T(string n):name(n){};
};
bool operator < (const T& l, const T *r) {
return l.name < r->name;
}
bool operator < (const T *l, const T &r) {
return l->name < r.name;
}
Now you can search like this:
bool has_3 = binary_search( t.begin(), t.end(), T("two") ) ;
if( has_3 ){
cout <<"Its there" << endl;
}
Demo 2.
It's a really dumb requirement to work with a vector of pointers to dynamically allocated objects. But here is an approach that will work.
#include <iostream>
#include <string>
#include <algorithm>
struct T
{
std::string name;
T(std::string n):name(n){};
};
// this is the comparater needed to work with pointers, but it should
// NOT be a member of T
bool pointer_comparer(const T *left, const T *right)
{
// this assumes both left and right point to valid objects
return left->name < right->name;
}
int main()
{
std::vector<T *> t;
t.push_back(new T("one"));
t.push_back(new T("two"));
t.push_back(new T("three"));
// t is unsorted. We need to sort it since binary_search will
// ASSUME it is sorted
std::sort(t.begin(), t.end(), pointer_comparer);
T *value_needed = new T("two");
bool has_3 = std::binary_search( t.begin(), t.end(), value_needed, pointer_comparer);
if(has_3)
{
std::cout <<"Its there" << std::endl;
}
// since we've been stupidly allocating objects, we need to release them
delete value_needed;
for (std::vector<T *>::iterator i = t.begin(), end = t.end();
i != end; ++i)
{
delete (*i);
}
// and since t now contains a set of dangling pointers, we need to discard them too
t.resize(0);
return 0;
}
Why do I say the requirement to work with a vector of pointers to dynamically allocated objects. Compare the above with an approach that works with a vector<T> rather than a vector<T *>.
#include <iostream>
#include <string>
#include <algorithm>
struct T
{
std::string name;
T(std::string n):name(n){};
bool operator < (const T &) const
{
return name < n.name;
};
};
int main()
{
std::vector<T> t;
t.push_back(T("one"));
t.push_back(T("two"));
t.push_back(T("three"));
// t is unsorted. We need to sort it since binary_search will
// ASSUME it is sorted
std::sort(t.begin(), t.end());
bool has_3 = std::binary_search(t.begin(), t.end(), T("two"));
if(has_3)
{
std::cout <<"Its there" << std::endl;
}
// we need do nothing here. All objects use above will be properly released
return 0;
}
Note: I've written the above so it works with ALL C++ standards. Assuming C++11 and later, simplifications are possible in both cases.

how to use boost::unordered_map

for my application, i need to use a hash map, so i have written a test program in which i store some instances of a baseclass in a boost::unordered_map. but i want to reach the instances by calling special functions which return a derived class of the base and i use those functions' parameters for hash key of unordered_map. if no class is found with certain parameters then a class is generated and stored in map. the purpose of the program may not be clear but here is the code.
#include <boost/unordered_map.hpp>
#include <iostream>
using namespace std;
using namespace boost;
typedef unsigned char BYT;
typedef unsigned long long ULL;
class BaseClass
{
public:
int sign;
size_t HASHCODE;
BaseClass(){}
};
class ClassA : public BaseClass
{
public:
int AParam1;
int AParam2;
ClassA(int s1, int s2) : AParam1(s1), AParam2(s2)
{
sign = AParam1;
}
};
struct HashKey
{
ULL * hasharray;
size_t hashNum;
size_t HASHCODE;
HashKey(ULL * ULLarray, size_t Hashnum) : hasharray(ULLarray), hashNum(Hashnum), HASHCODE(0)
{ }
bool operator == (const HashKey & hk ) const
{
bool deg = (hashNum == hk.hashNum);
if (deg)
{
for (int i = 0; i< hashNum;i++)
if(hasharray[i] != hk.hasharray[i]) return false;
}
return deg;
}
};
struct ihash : std::unary_function<HashKey, std::size_t>
{
std::size_t operator()(HashKey const & x) const
{
std::size_t seed = 0;
if (x.hashNum == 1)
seed = x.hasharray[0];
else
{
int amount = x.hashNum * 8;
const std::size_t fnv_prime = 16777619u;
BYT * byt = (BYT*)x.hasharray;
for (int i = 0; i< amount;i++)
{
seed ^= byt[0];
seed *= fnv_prime;
}
}
return seed;
}
};
typedef std::pair<HashKey,BaseClass*> HashPair;
unordered_map<HashKey,BaseClass*,ihash> UMAP;
typedef unordered_map<HashKey,BaseClass*,ihash>::iterator iter;
BaseClass * & FindClass(ULL* byt, int Num, size_t & HCode)
{
HashKey hk(byt,Num);
HashPair hp(hk,0);
std::pair<iter,bool> xx = UMAP.insert(hp);
// if (xx.second) UMAP.rehash((UMAP.size() + 1) / UMAP.max_load_factor() + 1);
if (!xx.first->second) HCode = UMAP.hash_function()(hk);
return xx.first->second;
}
template <typename T, class A,class B>
T* GetClass(size_t& hashcode ,A a, B b)
{
ULL byt[3] = {a,b,hashcode};
BaseClass *& cls = FindClass(byt, 3, hashcode);
if(! cls){ cls = new T(a,b); cls->HASHCODE = hashcode;}
return static_cast<T*>(cls);
}
ClassA * findA(int Period1, int Period2)
{
size_t classID = 100;
return GetClass<ClassA>(classID,Period1,Period2);
}
int main(int argc, char* argv[])
{
int limit = 1000;
int modnum = 40;
int result = 0;
for(int i = 0 ; i < limit; i++ )
{
result += findA( rand() % modnum ,4)->sign ;
}
cout << UMAP.size() << "," << UMAP.bucket_count() << "," << result << endl;
int x = 0;
for(iter it = UMAP.begin(); it != UMAP.end(); it++)
{
cout << ++x << "," << it->second->HASHCODE << "," << it->second->sign << endl ;
delete it->second;
}
return 0;
}
the problem is, i expect that the size of UMAP is equal to modnum however it is allways greater than modnum which means there are more than one instance that has the same parameters and HASHCODE.
what is the solution to my problem? please help.
thanks
Here are a couple of design problems:
struct HashKey
{
ULL * hasharray;
...
Your key type stores a pointer to some array. But this pointer is initialized with the address of a local object:
BaseClass * & FindClass(ULL* byt, int Num, size_t & HCode)
{
HashKey hk(byt,Num); // <-- !!!
HashPair hp(hk,0);
std::pair<iter,bool> xx = UMAP.insert(hp);
if (!xx.first->second) HCode = UMAP.hash_function()(hk);
return xx.first->second;
}
template <typename T, class A,class B>
T* GetClass(size_t& hashcode ,A a, B b)
{
ULL byt[3] = {a,b,hashcode}; // <-- !!!
BaseClass *& cls = FindClass(byt, 3, hashcode);
if(! cls){ cls = new T(a,b); cls->HASHCODE = hashcode;}
return static_cast<T*>(cls);
}
This makes the map store a HashKey object with a dangling pointer. Also you are returning a reference to a member of a function local object called xx in FindClass. The use of this reference invokes undefined behaviour.
Consider renaming the map's key type. The hash code itself shouldn't be a key. And as your operator== for HashKey suggests, you don't want the actual key to be the hash code but the sequence of integers of variable length. Also, consider storing the sequence inside of the key type instead of a pointer, for example, as a vector. In addition, avoid returning references to function local objects.
Using unordered_map does not guarantee that you do not get has collisions, which is what you describe here.
there are more than one instance that
has the same parameters and HASHCODE
You can tune your hashing algorithm to minimize this, but in the (inevitable) collision case, the hash container extends the list of objects in the bucket corresponding to that hashcode. Equality comparison is then used to resolve the collision to a specific matching object. This may be where your problem lies - perhaps your operator== does not properly disambiguate similar but not identical objects.
You cannot expect one object per bucket, or the container would grow unbounded in large collection size cases.
btw if you are using a newer compiler you may find it supports std::unordered_map, so you can use that (the official STL version) instead of the Boost version.

functor returning 0

I've recently started teaching myself the standard template library. I was curious as to why the GetTotal() method in this class is returning 0?
...
class Count
{
public:
Count() : total(0){}
void operator() (int val){ total += val;}
int GetTotal() { return total;}
private:
int total;
};
void main()
{
set<int> s;
Count c;
for(int i = 0; i < 10; i++) s.insert(i);
for_each(s.begin(), s.end(), c);
cout << c.GetTotal() << endl;
}
for_each takes the function by-value. That is, it uses a copy of the functor and not the functor itself. Your local c is left unchanged.
for_each returns the functor it used, though, so you could do:
Count c;
c = for_each(s.begin(), s.end(), c);
Or more idiomatically:
Count c = for_each(s.begin(), s.end(), Count());
However, there exists such functionality already (no need for your functor):
int total = std::accumulate(s.begin(), s.end(), 0);