C++ comparison using strings for operators - c++

I was writing out the function below, and started to think that there's probably a better way to go about it; however Google isn't turning up much, so any insight would be appreciated. I also have a very similar situation involving integers.
bool compare_strs (std::string operator_, std::string str_0, std::string str_1)
{
if (operator_ == ">")
{
return str_0 > str1;
}
else if (operator_ == "<")
{
return str_0 < str1;
}
else if (operator_ == "<=")
{
return str_0 <= str1;
}
else
{
return str_0 >= str1;
}
}

You can use a map to store operators and related functors. In C++11, something along these lines should work, though there might be a couple subtle errors. In C++03, you'll have to change a couple things, including changing std::function to boost::function or function pointers, as well as using std::make_pair to store the map values.
#include <functional> //for std::function and std::less et al.
#include <map> //for std::map
#include <stdexcept> //for std::invalid_argument
#include <string> //for std::string
struct StringComparer {
static bool compare( //split up to fit width
const std::string &oper,
const std::string &str0, const std::string &str1
) {
MapType::const_iterator iter = operations.find(oper);
if (iter == std::end(operations)) //check if operator is found
throw std::invalid_argument("No match for provided operator.");
return iter->second(str0, str1); //call the appropriate functor
}
private:
using MapType = std::map< //makes life easier, same as typedef
std::string,
std::function<bool(const std::string &, const std::string &)>
>;
static const MapType operations; //a map of operators to functors
};
const StringComparer::MapType StringComparer::operations = { //define the map
{"<", std::less<std::string>()}, //std::less is a functor version of <
{"<=", std::less_equal<std::string>()},
{">", std::greater<std::string>()},
{">=", std::greater_equal<std::string>()}
};
You can also see it in action. The nice thing about an approach like this is that it's very easy to include more operators, as all you have to do is add them to the map.

As others have mentioned, you should first ask yourself why you are doing this - there is likely a better solution. Going with this though, I might do something like:
template <typename T1, typename T2>
bool mycompare(std::string operator_, const T1 & _lhs, const T2 & _rhs)
{
if (operator_ == ">")
{
return _lhs > _rhs;
}
else if (operator_ == "<")
{
return _lhs < _rhs;
}
//etc.
else
{
throw new exception("Invalid operator");
}
}

Related

Static Code Analysis/Linting: Warn about == being called on unordered_set<K, H, CustomEquality>

For an unordered_set using a custom equality function, that equality definition is ignored when two sets are compared. Example:
#include <iostream>
#include <unordered_set>
#include <string>
struct Hash {
size_t operator()(const std::string& str) const {
return 0;
}
};
struct InsensitiveCompare {
bool operator() (const std::string& str1, const std::string& str2) const {
// https://stackoverflow.com/a/43226907/2204581
return str1.size() == str2.size() && std::equal(str1.begin(), str1.end(), str2.begin(), [](auto a, auto b){return std::tolower(a)==std::tolower(b);});
}
};
int main()
{
std::unordered_set<std::string, Hash, InsensitiveCompare> s1, s2;
s1.emplace("test");
s2.emplace("Test");
std::cout << (s1 == s2 ? "" : "not ") << "equal" << std::endl;
// prints "not equal"
return 0;
}
http://cpp.sh/2vmwc5
I do not understand why the custom equality function is not forwarded to std::is_permutation. operator== requires the comparator to have the same behavior on the left and right side, so either comparator could be used. If you ask me, this behavior is completely unexpected in the best case and wrong in the worst. However, this is unlikely to change.
My question is if there is any type of static code analysis/linting tool that would spot this. Neither -Weverything nor clang-tidy let me know about this.

Convert std::string to ci_string

I used this approach to create a case-insensitive typedef for string. Now, I'm trying to convert a std::string to ci_string. All of the following throw compiler errors:
std::string s {"a"};
ci_string cis {s};
ci_string cis (s);
ci_string cis {(ci_string)s};
ci_string cis ((ci_string)s);
ci_string cis = s;
I spent some time trying to figure out how to overload the = operator, and I attempted to use static_cast and dynamic_cast without success. How can I do this?
Your two types are different, so you cannot use the constructor with a regular std::string. But your string is still able to copy a C string, so this should work:
std::string s{"a"};
ci_string cis{ s.data() }; // or s.c_str(), they are the same
std::string and ci_string are unrelated types. Why would static_cast or dynamic_cast be able to convert them? Remember: Two different instantiations of the same template are unrelated types and are potentially completely incompatible.
Give up on the idea of overloading operator= or on some magic that performs the conversion automatically. You have two unrelated types. But they both offer member functions that can you can successfully use to copy the char elements from one to the other.
Just write a simple conversion function that takes advantage of the fact that both std::string and ci_string have their value_type defined as char, and appropriately use one of std::basic_string's constructors, either one which takes a pointer to raw data or one which takes two iterators which form a range.
Here is a complete example:
#include <string>
#include <iostream>
struct ci_char_traits : public std::char_traits<char> {
static bool eq(char c1, char c2) { return toupper(c1) == toupper(c2); }
static bool ne(char c1, char c2) { return toupper(c1) != toupper(c2); }
static bool lt(char c1, char c2) { return toupper(c1) < toupper(c2); }
static int compare(const char* s1, const char* s2, size_t n) {
while( n-- != 0 ) {
if( toupper(*s1) < toupper(*s2) ) return -1;
if( toupper(*s1) > toupper(*s2) ) return 1;
++s1; ++s2;
}
return 0;
}
static const char* find(const char* s, int n, char a) {
while( n-- > 0 && toupper(*s) != toupper(a) ) {
++s;
}
return s;
}
};
typedef std::basic_string<char, ci_char_traits> ci_string;
ci_string to_ci_string(std::string const& src)
{
return ci_string(src.begin(), src.end());
// or:
// return ci_string(src.c_str());
}
int main()
{
std::string s {"a"};
auto cis = to_ci_string(s);
std::cout << cis.c_str() << "\n";
}

boost::any_range and operator []

Consider the following code:
#include <boost/range.hpp>
#include <boost/range/any_range.hpp>
#include <boost/range/join.hpp>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <list>
struct TestData {
TestData() : m_strMem01("test"), m_intMem02(42), m_boolMem03(true) {}
std::string m_strMem01;
int m_intMem02;
bool m_boolMem03;
};
struct IntComp {
bool operator()(const TestData &s, int i) { return s.m_intMem02 < i; }
bool operator()(int i, const TestData &s) { return i < s.m_intMem02; }
bool operator()(const TestData &i, const TestData &s) {
return i.m_intMem02 < s.m_intMem02;
}
};
struct StrComp {
bool operator()(const TestData &s, const std::string &str) {
return s.m_strMem01 < str;
}
bool operator()(const std::string &str, const TestData &s) {
return str < s.m_strMem01;
}
bool operator()(const TestData &i, const TestData &s) {
return i.m_strMem01 < s.m_strMem01;
}
};
typedef boost::any_range<TestData, boost::forward_traversal_tag,
const TestData &, std::ptrdiff_t> TestRange;
std::vector<TestData> vecData(10);
std::list<TestData> listData(20);
TestRange foo() {
TestRange retVal;
auto tmp1 = std::equal_range(vecData.cbegin(), vecData.cend(), 42, IntComp());
retVal = boost::join(retVal, tmp1);
auto tmp2 =
std::equal_range(listData.cbegin(), listData.cend(), "test", StrComp());
retVal = boost::join(retVal, tmp2);
return retVal;
}
int main(int argc, char *argv[]) {
auto res = foo();
for (auto a : res) {
std::cout << a.m_strMem01 << std::endl;
}
//std::cout << res[4].m_intMem02 << std::endl;
}
If you uncomment the last line the code fails since distance_to not implemented for any_forward_iterator_interface. I'm not sure what exactly I'm missing here, like implementing operator[] or distance_to but for what? My own version traversal tag? And why it doesn't work in the first place?
Coliru version
I would say the answer depends on your performance needs and your laziness when it comes to implementing a new iterator abstraction. The core reason for your [] operator not working is the fact that std::list<...> does not provide a random access traversal iterator. If you would have chosen a container that provides such an iterator. You any_range<...> could have taken the random_access_traversal_tag and everything would be fine.
I think it's fair to say that it is not such a big deal to implement a random access iterator on top of a list by simply encapsulating the current index and count forward and backward within the list whenever a specific position is meant to be accessed, but it's clearly against the nature of the list performance-wise.
Is there a good reason to hold one of the collection in a list ?
Is there a good reason to access the resulting any_range by random ?
Is it worth the effort to provide a inefficient random access interface for std::list ?
Of course any_iterator (which underlies the any_range implementation) doesn't gratuitously emulate RandomAccess iterators for any odd iterator you pass.
If you want that, just make an iterator adaptor that does this (making it very slow to random access elements in a list - so don't do this).

Iterating in a sorted manner over a std::vector<std::pair<T,U> > object

I am reading a object from a database of type Foo, as defined below. This object is a vector of Foo Members, where a Foo Members consists of a string id and a container object.
typedef std::pair<std::string, Container> FooMember;
typedef std::vector<FooMember> Foo;
I wish to iterate over a Foo object in its sorted form, where sorting is done with respect to the id. To do this I am using the following function to create first a sorted version of the object. As you can see, the object is sorted in a case insensitive manner. Is there a better way for me to iterate over this object compared to how I am currently doing it?
Foo sortedFoo(Foo& value) const {
Foo returnValue;
returnValue.reserve(value.size());
// use a map to sort the items
std::map<std::string, FooMember> sortedMembers;
{
Foo::iterator i = value.begin();
Foo::iterator end = value.end();
for(; i!=end; ++i) {
std::string name = i->first;
boost::algorithm::to_lower(name);
sortedMembers[name] = *i;
}
}
// convert the map to a vector of its values in sorted order
std::map<std::string, FooMember >::iterator i = sortedMembers.begin();
std::map<std::string, FooMember >::iterator end = sortedMembers.end();
for(; i!=end; ++i) {
returnValue.push_back(i->second);
}
return returnValue;
}
Yes: Copy the vector, then use std::sort with a custom comparison predicate:
struct ByIdCaseInsensitive {
bool operator ()(const FooMember& lhs, const FooMember& rhs) const {
return boost::algorithm::to_lower_copy(lhs.first) <
boost::algorithm::to_lower_copy(rhs.first);
}
};
Way more efficient than filling a map, and then copying back to a vector.
The predicate would be even better if it used a proper Unicode collation algorithm, but that isn't available in the standard library or Boost.
You can use std::sort
#include <algorithm>
bool comparator(const FooMember& i, const FooMember& j)
{
std::string str1 = i.first;
boost::algorithm::to_lower(str1);
std::string str2 = j.first;
boost::algorithm::to_lower(str2);
return (str1 < str2);
}
void sortFoo(Foo& value) {
std::sort (value.begin(), value.end(), comparator);
}
Or, you can keep Foo objects in a std::map<std::string, Foo> from the beginning so they remain always sorted.
The best way would be to use std::sort with a custom comparator for FooMembers:
bool cmp(const FooMember& lhs, const FooMember& rhs);
Foo sortedFoo(const Foo& value) const
{
Foo tmp = value;
return std::sort(tmp.begin(), tmp.end(), cmp);
}
where the comparison can be implemented with the help of std::lexicographical_compare and tolower:
#include <cctype> // for std::tolower
bool ci_cmp(char a, char b)
{
return std::tolower(a) < std::tolower(b);
}
#include <algorithm> // for std::sort, std::lexicographical_compare
bool cmp(const FooMember& lhs, const FooMember& rhs)
{
return std::lexicographical_compare(lhs.first.begin(),
lhs.first.end(),
rhs.first.begin(),
rhs.first.end(),
ci_cmp);
}
You can also use std::sort with a lambda expression:
std::sort(value.begin(), value.end(), [](const FooMember &lhs, const FooMember &rhs)
{
std::string str1 = i.first, str2 = j.first;
boost::algorithm::to_lower(str1);
boost::algorithm::to_lower(str2);
return str1 < str2;
});
Or use the version provided by erelender. It's up to you.
Semantically std::vector<std::pair<T,U> > is a std::map<T,U> (but implementations are usually different). If you can re-design Foo, you probably better do it. As side effect, you will get sorting for free.
typedef std::map<std::string, Container> Foo;

How to combine a function and a predicate in for_each?

How can you call a Function over some part of a container, using for_each() ?
I have created a for_each_if() to do a
for( i in shapes )
if( i.color == 1 )
displayShape(i);
and the call looks like
for_each_if( shapes.begin(), shapes.end(),
bind2nd( ptr_fun(colorEquals), 0 ),
ptr_fun( displayShape ) );
bool colorEquals( Shape& s, int color ) {
return s.color == color;
}
However, I feel immitating STL-like algorithms is not something that I should be doing.
Is there a way to use only existing STL keywords to produce this ?
I did not want to do a
for_each( shapes.begin(), shapes.end(),
bind2nd( ptr_fun(display_shape_if_color_equals), 0 ) );
because, in a more complicated case, the functor name would be misleading with respect to what the functor
*Is there a way to access a struct's member (like colorEquals) for functions like for_each without having to create a function ? *
Imitating STL-like algorithms is exactly what you should be doing. That's why they're in the STL.
Specifically, you can use a functor instead of creating an actual function and binding it. This is much neater, really.
template<typename Iterator, typename Pred, typename Operation> void
for_each_if(Iterator begin, Iterator end, Pred p, Operation op) {
for(; begin != end; begin++) {
if (p(*begin)) {
op(*begin);
}
}
}
struct colorequals {
colorequals(int newcol) : color(newcol) {}
int color;
bool operator()(Shape& s) { return s.color == color; }
};
struct displayshape {
void operator()(Shape& s) { // display the shape }
};
for_each_if(shapes.begin(), shapes.end(), colorequals(0), displayshape());
This is usually considered the idiomatic way to go.
Using boost range adaptors is much neater.
using boost::adaptor::filtered;
using boost::bind;
class Shape {
int color() const;
};
void displayShape(const Shape & c);
bool test_color(const Shape & s, int color ){
return s.color() == color;
}
boost::for_each
( vec | filtered(bind(&test_color, _1, 1)
, bind(&displayShape, _1)
)
Note the use of the new range library to abstract away
iterators in favor of ranges and the range adaptors
library to compose a pipeline of operations.
All the standard stl iterator based algorithms have
been ported to range based algorithms.
Imagine this
typedef boost::unordered_map<int, std::string> Map;
Map map;
...
using boost::adaptor::map_keys;
using boost::bind
using boost::ref
using boost::adaptor::filtered;
bool gt(int a, int b)
{ return a > b };
std::string const & get(const Map & map, int const & a)
{ return map[a] }
// print all items from map whose key > 5
BOOST_FOREACH
( std::string const & s
, map
| map_keys
| filtered(bind(&gt, _1, 5))
| transformed(bind(&get, ref(map), _1))
)
{
cout << s;
}
Read Range Adaptors and Range Algorithm.
You can use the C++20 ranges. Here an example where we add one to all even numbers of a std::vector
#include <ranges>
#include <algorithm>
#include <vector>
namespace ranges = std::ranges;
std::vector<int> vec = {1, 2, 3, 4, 5};
const auto even = [](int i) { return 0 == i % 2; };
ranges::for_each(vec | std::views::filter(even), [](int& i){ i+=1;});
You can find a living example on compiler explorer here
To use a regular for_each with an if you would need a Functor that emulates an if condition.
#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
#include <boost/bind.hpp>
using namespace std;
struct incr {
typedef void result_type;
void operator()(int& i) { ++i; }
};
struct is_odd {
typedef bool return_type;
bool operator() (const int& value) {return (value%2)==1; }
};
template<class Fun, class Cond>
struct if_fun {
typedef void result_type;
void operator()(Fun fun, Cond cond, int& i) {
if(cond(i)) fun(i);
}
};
int main() {
vector<int> vec;
for(int i = 0; i < 10; ++i) vec.push_back(i);
for_each(vec.begin(), vec.end(), boost::bind(if_fun<incr, is_odd>(), incr(), is_odd(), _1));
for(vector<int>::const_iterator it = vec.begin(); it != vec.end(); ++it)
cout << *it << " ";
}
Unfortunately my template hackery isn't good enough to manage this with bind1st and bind2nd as it somehow gets confusing with the binder being returned being a unary_function but it looks pretty good with boost::bind anyhow. My example is no means perfect as it doesn't allow the Func passed into if_fun to return and I guess somebody could point out more flaws. Suggestions are welcome.