Error with a predicate in std::remove_if - c++

I basically have an std::deque of objects, and I want to remove some of these objets according a condition on a given member variable of the objects, so that I use a predicate, but I have errors that I don't really understand.
I am using g++ with the -std=c++11 for STL reasons (shared pointers) but I was trying to work this out on windows with MVS with non c++11 code, so that I am looking for a non c++11 solution, without lamdas etc
The code is :
#include <iostream> // for std::cout and std::endl
#include <cstdio> // for getchar()
#include <memory> // for std::shared_ptr
#include <deque> // for std::deque
#include <algorithm> // for std::earse and std::remove_if
class A
{
private:
int _i;
double _d;
public:
A(int i, double d)
{
_i = i;
_d = d;
}
int geti()const
{
return _i;
}
double getValueOnWhichToCheck()const
{
return _d;
}
};
typedef std::shared_ptr<A> A_shared_ptr;
typedef std::deque<A_shared_ptr> list_type;
void PrintDeque(list_type & dq)
{
if (0 == dq.size())
{
std::cout << "Empty deque." << std::endl;
}
else
{
for (int i = 0 ; i < dq.size() ; ++i)
{
std::cout << i+1 << "\t" << dq[i] << std::endl;
}
}
}
class B
{
public:
double getThreshold() // Non constant for a reason as in real code it isn't
{
return 24.987; // comes from a calculation not needed here so I return a constant.
}
bool Predicate(A_shared_ptr & a)
{
return a->getValueOnWhichToCheck() >= getThreshold();
}
void DoStuff()
{
A_shared_ptr pT1 = std::make_shared<A>(A(2, -6.899987));
A_shared_ptr pT2 = std::make_shared<A>(A(876, 889.878762));
A_shared_ptr pT3 = std::make_shared<A>(A(-24, 48.98924));
A_shared_ptr pT4 = std::make_shared<A>(A(78, -6654.98980));
A_shared_ptr pT5 = std::make_shared<A>(A(6752, 3.141594209));
list_type dq = {pT1,pT2,pT3,pT4,pT5};
PrintDeque(dq);
bool (B::*PtrToPredicate)(A_shared_ptr &) = &B::Predicate;
dq.erase(std::remove_if(dq.begin(), dq.end(), PtrToPredicate),dq.end());
PrintDeque(dq);
}
};
int main()
{
B * pB = new B();
pB->DoStuff();
getchar();
}
and the output of g++ -std=c++11 main.cpp -o main is :
In file included from /usr/include/c++/5/bits/stl_algobase.h:71:0,
from /usr/include/c++/5/bits/char_traits.h:39,
from /usr/include/c++/5/ios:40,
from /usr/include/c++/5/ostream:38,
from /usr/include/c++/5/iostream:39,
from main.cpp:1:
/usr/include/c++/5/bits/predefined_ops.h: In instantiation of ‘bool __gnu_cxx::__ops::_Iter_pred<_Predicate>::operator()(_Iterator) [with _Iterator = std::_Deque_iterator<std::shared_ptr<A>, std::shared_ptr<A>&, std::shared_ptr<A>*>; _Predicate = bool (B::*)(std::shared_ptr<A>&)]’:
/usr/include/c++/5/bits/stl_algo.h:866:20: required from ‘_ForwardIterator std::__remove_if(_ForwardIterator, _ForwardIterator, _Predicate) [with _ForwardIterator = std::_Deque_iterator<std::shared_ptr<A>, std::shared_ptr<A>&, std::shared_ptr<A>*>; _Predicate = __gnu_cxx::__ops::_Iter_pred<bool (B::*)(std::shared_ptr<A>&)>]’
/usr/include/c++/5/bits/stl_algo.h:936:30: required from ‘_FIter std::remove_if(_FIter, _FIter, _Predicate) [with _FIter = std::_Deque_iterator<std::shared_ptr<A>, std::shared_ptr<A>&, std::shared_ptr<A>*>; _Predicate = bool (B::*)(std::shared_ptr<A>&)]’
main.cpp:67:73: required from here
/usr/include/c++/5/bits/predefined_ops.h:234:30: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘((__gnu_cxx::__ops::_Iter_pred<bool (B::*)(std::shared_ptr<A>&)>*)this)->__gnu_cxx::__ops::_Iter_pred<bool (B::*)(std::shared_ptr<A>&)>::_M_pred (...)’, e.g. ‘(... ->* ((__gnu_cxx::__ops::_Iter_pred<bool (B::*)(std::shared_ptr<A>&)>*)this)->__gnu_cxx::__ops::_Iter_pred<bool (B::*)(std::shared_ptr<A>&)>::_M_pred) (...)’
{ return bool(_M_pred(*__it)); }
^

Not a fan of function pointers, but using lambda's this seems to compile... https://ideone.com/0StRcw
dq.erase(std::remove_if(dq.begin(), dq.end(), [this](const std::shared_ptr<A>& a) {
return a->getValueOnWhichToCheck() >= getThreshold();
}),dq.end());
Or without lambdas..
auto predicateToUse = std::bind(&B::Predicate, this, std::placeholders::_1);
dq.erase(std::remove_if(dq.begin(), dq.end(), predicateToUse), dq.end());
Update no auto:
dq.erase(std::remove_if(dq.begin(), dq.end(), std::bind(&B::Predicate, this, std::placeholders::_1)), dq.end());

You are not going to be able to use a non static member function pointer with remove_if. remove_if requires a normal function pointer, static member function pointer or a function object. The reason it cannot use a member function pointer is because it needs an instance of the class in order to be able to call the function and you cannot wrap that into the call
You are either need to make Predicate static, create a function object and pass an instance of that, or use a lambda in the call site.

Related

Elegant way to find if a class instance is contained in a std::vector (C++)

I want to construct a vector that will hold int M elements of my class Point which represent a single point (x,y) where both x,y are int.
The points that will be stored in this vector are randomly generated. However, I do not want to store the same point more than once.
My implementation is the following:
#include <algorithm>
#include <random>
std::random_device rd;
std::mt19937 mt(rd());
std::vector<Point> InitList(int M)
{
if ( M <=0 )
{
std::cout << "InitList: Length of vector must be greater than zero. Don't play around!"
<< std::endl;
exit(EXIT_FAILURE);
}
Point to_erase(-100,-100); // <----- can I get rid of this?
Point to_add;
int x,y;
std::vector<Point> vec = {};
vec.push_back(to_erase); // <----- and this
while (vec.size() < M+1) // <----- and then M+1 -> M
{
std::uniform_int_distribution<int> RandInt(0,100);
x = RandInt(mt);
y = RandInt(mt);
point.set(x,y);
if ( std::find(vec.begin(), vec.end(),to_add) == vec.end() )
vec.push_back(to_add);
}
vec.erase (vec.begin()); // <----- and finally avoid this?
return vec;
}
How can I avoid the trick of adding the first instance to_erase?
EDIT
So I changed the implementation to use std::unordered_set
and I am getting the following errors:
error: use of deleted function ‘std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set() [with _Value = Site; _Hash = std::hash<Site>; _Pred = std::equal_to<Site>; _Alloc = std::allocator<Site>]’
note: ‘std::hash<Site>::hash()’ is implicitly deleted because the default definition would be ill-formed:
101 | struct hash : __hash_enum<_Tp>
| ^~~~
/usr/include/c++/9/bits/functional_hash.h:101:12: error: no matching function for call to ‘std::__hash_enum<Site, false>::__hash_enum()’
/usr/include/c++/9/bits/functional_hash.h:82:7: note: candidate: ‘std::__hash_enum<_Tp, <anonymous> >::__hash_enum(std::__hash_enum<_Tp, <anonymous> >&&) [with _Tp = Site; bool <anonymous> = false]’
82 | __hash_enum(__hash_enum&&);
| ^~~~~~~~~~~
And the list goes on. I guess there is an implementation missing in my class Point. What should I do?
You can guarantee uniqueness just by using std::set (https://www.cplusplus.com/reference/set/set/) instead of std::vector.
If order doesn't matter, use std::unordered_set.
Regarding the hash issues you're seeing -- you have to define a hash function for a custom type, when using a few C++ types like set, unordered_set, map, etc. This is a function that returns a unique number based on the value of your custom Point instance. I stubbed out this example to give you an idea of how this might work for you, but YMMV:
#include <iostream>
#include <unordered_set>
struct Point
{
int x;
int y;
Point(int x, int y): x(x), y(y) {};
};
bool operator==(const Point& lhs, const Point& rhs)
{
return ((lhs.x == rhs.x) and (lhs.y == rhs.y));
}
namespace std
{
template <>
struct hash<Point>
{
size_t operator()( const Point& p ) const
{
return (p.x << 32 | p.y);
}
};
}
int main() {
Point p1(3,2);
Point p2(3,2);
std::unordered_set<Point> someSet;
someSet.insert(p1);
std::cout << "set size:" << someSet.size() << std::endl;
// set size: 1
someSet.insert(p2);
std::cout << "set size:" << someSet.size() << std::endl;
// set size: 1 (still!)
return 0;
}
If only presence is relevant: std::set.
If you need also occurrence count, you could use std::map<Point, int>, where the int is the count. With some special handling to remove elements when counts gets to zero.
You could replace std::map with std::unordered_map for performance reasons. But ALWAYS test both before deciding.

No matching member function call to 'push_back', vector of shared pointers

I have a Container class that is meant to store a vector of shared pointers. Whenever an item is appended to the Container, I want it to assume ownership of that item. In other words, when the Container is deconstructed, all of the elements inside it should also be deconstructed.
template <typename T>
class Container
{
private:
const std::vector<std::shared_ptr<T>> vec_;
public:
void append(std::shared_ptr<T> item)
{
vec_.push_back(std::move(item));
}
void printElements()
{
for (int i = 0; i < vec_.size(); i++) { std::cout << vec_[i] << std::endl; }
}
};
int main(int argc, char** argv) {
std::unique_ptr<Container<std::string>> c = std::make_unique<Container<std::string>>();
c->append(std::make_shared<std::string>("hello"));
return 0;
}
The problem is, I get the following error on vec_.push_back(std::move(item)).
No matching member function for call to 'push_back'
I'm not sure why this error is occurring.
Original answer:
Your std::vector, vec_ is const. Seems to me that would forcefully remove or disable any method, such as push_back(), which tries to modify the const vector.
Additions added 10 May 2020:
I think it's worth expounding upon this comment from #Carpetfizz in the comments under his question, too:
#GabrielStaples thanks, removing const did the trick. I thought const only protected against reassignment of vector_ but allowed for it to be mutated?
My response:
No, const here applies to the contents of the vector. The vector isn't a pointer, but what you're talking about could be done with pointers, by making the pointer itself const instead of the contents of what it points to const. Also, const and mutable are opposites. One undoes the other. You cannot have both in effect at the same time. By definition, something constant is immutable (unchangeable), and something mutable is non-constant.
And how might one make a pointer const but not the contents of what it points to?
First, consider the original code (with some minor modifications/fixes I did to it):
Run it yourself online here: https://onlinegdb.com/SyMqoeU9L
1) cpp_template_const_vector_of_smart_ptrs_test_BEFORE.cpp:
#include <iostream>
#include <memory>
#include <vector>
template <typename T>
class Container
{
private:
// const std::vector<std::shared_ptr<T>> vec_; // does NOT work
std::vector<std::shared_ptr<T>> vec_; // works!
public:
void append(std::shared_ptr<T> item)
{
vec_.push_back(std::move(item));
}
void printElements()
{
for (int i = 0; i < vec_.size(); i++)
{
// Don't forget to dereference the pointer with `*` in order to
// obtain the _contens of the pointer_ (ie: what it points to),
// rather than the pointer (address) itself
std::cout << *vec_[i] << std::endl;
}
}
};
int main(int argc, char** argv)
{
std::unique_ptr<Container<std::string>> c = std::make_unique<Container<std::string>>();
c->append(std::make_shared<std::string>("hello"));
c->append(std::make_shared<std::string>("world"));
c->printElements();
return 0;
}
Output:
hello
world
And here's the new code demonstrating how to make a constant pointer to a non-const vector. See my comments here, and study the changes:
Run it yourself online here: https://onlinegdb.com/HyjNx-L5U
2) cpp_template_const_vector_of_smart_ptrs_test_AFTER.cpp
#include <iostream>
#include <memory>
#include <vector>
template <typename T>
class Container
{
private:
// const std::vector<std::shared_ptr<T>> vec_; // does NOT work
// Create an alias to this type just to make the creation below less
// redundant in typing out the long type
using vec_type = std::vector<std::shared_ptr<T>>;
// NON-const object (vector)--so it can be changed
vec_type vec_;
// const pointer to NON-const object--so, vec_p_ can NOT be re-assigned to
// point to a new vector, because it is `const`! But, **what it points to**
// CAN be changed because it is NOT const!
vec_type * const vec_p_ = &vec_;
// This also does NOT work (in place of the line above) because it makes
// the **contents of what you're pointing to const**, which means again
// that the contents of the vector can NOT be modified.
// const vec_type * const vec_p_ = &vec_; // does NOT work
// Here's the compile-time error in gcc when compiling for C++17:
// main.cpp: In instantiation of ‘void Container<T>::append(std::shared_ptr<_Tp>) [with T = std::basic_string<char>]’:
// <span class="error_line" onclick="ide.gotoLine('main.cpp',78)">main.cpp:78:53</span>: required from here
// main.cpp:61:9: error: passing ‘const vec_type {aka const std::vector >, std::allocator > > >}’ as ‘this’ argument discards qualifiers [-fpermissive]
// vec_p_->push_back(std::move(item));
// ^~~~~~
// In file included from /usr/include/c++/7/vector:64:0,
// from main.cpp:22:
// /usr/include/c++/7/bits/stl_vector.h:953:7: note: in call to ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = std::shared_ptr >; _Alloc = std::allocator > >; std::vector<_Tp, _Alloc>::value_type = std::shared_ptr >]’
// push_back(value_type&& __x)
// ^~~~~~~~~
// To prove that vec_p_ can NOT be re-assigned to point to a new vector,
// watch this:
vec_type vec2_;
// vec_p_ = &vec2_; // COMPILE-TIME ERROR! Here is the error:
// main.cpp:44:5: error: ‘vec_p_’ does not name a type; did you mean ‘vec_type’?
// vec_p_ = &vec2_; // COMPILE-TIME ERROR!
// ^~~~~~
// vec_type
// BUT, this works just fine:
vec_type * vec_p2_ = &vec2_; // non-const pointer to non-const data
public:
void append(std::shared_ptr<T> item)
{
vec_p_->push_back(std::move(item));
}
void printElements()
{
for (int i = 0; i < vec_p_->size(); i++)
{
// Notice we have to use a double de-reference here now!
std::cout << *(*vec_p_)[i] << std::endl;
}
}
};
int main(int argc, char** argv)
{
std::unique_ptr<Container<std::string>> c = std::make_unique<Container<std::string>>();
c->append(std::make_shared<std::string>("hello"));
c->append(std::make_shared<std::string>("world"));
c->printElements();
return 0;
}
Output:
hello
world

C++ Passing multiple objects to a function in threads

I have a problem with threading and passing multiple objects of the same class to the function. It is an external function, inside which I want to call a class method over each of the passed objects.
I have tried passing a vector of objects, as well as a vector of pointers. I have also tried passing them by a reference.
Te specify, I have a "Gas" class, of which 3 objects are constructed in the code earlier. I call a lot of methods in threads before, so I guess that there is no issue with the class.
The compiler returns very long error, the one with _M_invoke(_Index_tuple<_Indices...>):
.In file included from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/thread:39:0,
from ./src/../input.h:22,
from ./src/mass_source.cpp:12:
C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/functional: In instantiation of 'struct std::_Bind_simple<void (*(int, std::reference_wrapper<std::vector<Ref::Gas> >))(int, std::vector<Ref::Gas>&)>':
C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/thread:142:59: required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, std::vector<Ref::Gas>&); _Args = {int&, std::reference_wrapper<std::vector<Ref::Gas, std::allocator<Ref::Gas> > >}]'
./src/mass_source.cpp:86:50: required from here
C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/functional:1505:61: error: no type named 'type' in 'class std::result_of<void (*(int, std::reference_wrapper<std::vector<Ref::Gas> >))(int, std::vector<Ref::Gas>&)>'
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/5.1.0/include/c++/functional:1526:9: error: no type named 'type' in 'class std::result_of<void (*(int, std::reference_wrapper<std::vector<Ref::Gas> >))(int, std::vector<Ref::Gas>&)>'
_M_invoke(_Index_tuple<_Indices...>)
The error message comes from my "original" code, in which I have commented out all of the lines, which are not presented below. As it was reported to be confusing, here is an explanation:
from ./src/../input.h:22 - inclusion of thread library
from ./src/mass_source.cpp:12: - inclusion of the above input.h file
./src/mass_source.cpp:86: - calc_ms fucntion call
Below the class declaration is attached:
namespace Ref{
class Gas{
public:
Gas(const int id, const int& imax, const int& jmax){
id_ = id;
NX_ = imax;
NR_ = jmax;
}
void set_ms(int m, double& mass_source){
ms_[m] = mass_source;
}
private:
int id_;
int NX_, NR_;
std::vector<double> ms_;
};
} //end of namespace
Code calling the function I have trouble with (creation of Gas objects and their pointers included):
using namespace Ref;
void calc_ms(int m, std::vector<Gas>& GAS);
int main(){
int i;
std::vector<Gas> gases;
std::vector<Gas*> ptr_gas(3);
for(i = 0; i < 3; i++){
gases.push_back(Gas(i, grid));
}
for(i = 0; i < 3; i++){
ptr_gas[i] = &gases[i];
}
std::vector<std::thread*> th_gas;
for(i = 0; i < 20; i++){
std::thread *thr = new std::thread(calc_ms, i, std::ref(gases));
th_gas.push_back(thr);
}
for(auto &X : th_gas){
X->join();
delete X;
}
th_gas.clear();
}
And the calc_ms fucntion definition:
using namespace Ref;
void calc_ms(int m, std::vector<Gas>& GAS){
double MassSourceCH4 = .... ;
double MassSourceCO = .... ;
double MassSourceCO2 = .... ;
GAS[0].set_ms(m, MassSourceCH4);
GAS[1].set_ms(m, MassSourceCO);
GAS[2].set_ms(m, MassSourceCO2);
}
I have also tried passing gases by copy, and ptr_gas as a reference and copy.
COMMENT: The ms_ member of Gas class is resized somwhere else in the code, thus the assignment with using indexes is not a problem.
Here is a simplified version showing how to use threads correctly and how to pass a vector to a thread by reference:
class Gas { /*...*/ };
void calc_ms(int m, std::vector<Gas>& gases);
int main() {
std::vector<Gas> gases;
std::vector<std::thread> th_gas;
for(int i = 0; i < 20; ++i)
th_gas.emplace_back(calc_ms, i, std::ref(gases));
for(auto& t : th_gas)
t.join();
th_gas.clear();
}

base operand has non pointer type g++ compiler error

In the following code, i am getting error:
city.cc: In member function ‘std::vector<std::basic_string<char> > MyCity::get_neighbours()’:
city.cc:25:42: error: base operand of ‘->’ has non-pointer type ‘std::pair<MyCity*, double>’
In file included from /depotbld/RHEL5.5/gcc-4.7.2/bin/../lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_algobase.h:65:0,
from /depotbld/RHEL5.5/gcc-4.7.2/bin/../lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/char_traits.h:41,
from /depotbld/RHEL5.5/gcc-4.7.2/bin/../lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/ios:41,
from /depotbld/RHEL5.5/gcc-4.7.2/bin/../lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/ostream:40,
from /depotbld/RHEL5.5/gcc-4.7.2/bin/../lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/iostream:40,
from city.cc:1:
/depotbld/RHEL5.5/gcc-4.7.2/bin/../lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_pair.h: In instantiation of ‘std::pair<_T1, _T2>::pair(const std::pair<_U1, _U2>&) [with _U1 = std::basic_string<char>; _U2 = double; _T1 = MyCity*; _T2 = double]’:
city.cc:18:50: required from here
/depotbld/RHEL5.5/gcc-4.7.2/bin/../lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_pair.h:111:39: error: cannot convert ‘const std::basic_string<char>’ to ‘MyCity*’ in initialization
.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class MyCity {
string name;
std::vector<pair<MyCity*,double> > neighbours;
public:
MyCity()
{
// neighbours.clear();
}
MyCity(string s, string s1, double d)
{
name = s;
neighbours.push_back(std::make_pair(s1,d));
}
std::vector<string> get_neighbours( )
{
std::vector<string> names;
for (size_t i = 0; i< neighbours.size(); ++i)
{
names.push_back(neighbours[i]->first->get_name());
}
return names;
}
};
class MyState {
vector<MyCity*> cities;
string name;
public:
MyState() { }
MyState(string s) {
name =s;
}
bool add_city(string name, string neigh, double d)
{
MyCity* c = new MyCity(name,neigh,d);
cities.push_back(c);
return true;
}
};
Do not dereference the std::pair as it is not a pointer.
std::vector<string> get_neighbours( )
{
std::vector<string> names;
for (size_t i = 0; i< neighbours.size(); ++i)
names.push_back(neighbours[i].first->get_name());
return names;
}
You also have a problem in your constructor, the std::make_pair(s1, d) will return a std::pair<std::string, double> so it cannot be pushed back to you neighbours vector.
Try something like that:
MyCtiy(string s)
:name(s)
{
}
MyCity(string s, string s1, double d)
:name(s)
{
created_neighbours.emplace_back(new neighbour(s1));
MyCity* city = created_neighbours.back().get();
neighbours.push_back(std::make_pair(city, d));
city->addNeighbour(this, d);
}
private:
std::vector<std::unique_ptr<MyCity>> created_neighbours;
void addNeighbour(MyCity* city, double d)
{
neighbours.push_back(std::make_pair(city, d));
}
Note: you can strip the addNeighbourd part if you don't want the association to be many-to-many.
Edit: Fix the addNeighbour to give a MyCity pointer. Created a created_neighbours collection to store (and free) created neighbours.
3 mistakes can be found in your code. Please find and correct the following 2 lines in your code
neighbours.push_back(std::make_pair(some_pointer_to_the_MyCity_object,d));
names.push_back(neighbours[i].first->get_name());
And you have to implement the get_name() function in MyCity class
Now it should be compiled

Defining new infix operators

So thanks to C++11, it's now possible to combine macros, user-defined literals, lambdas, etc. to create the closest I can get to 'syntactic sugar'. An example would be
if (A contains B)
Of course this is easy.
cout <<("hello"_s contains "ello"_s)<<endl;
The expression converts to a bool, where contains is a custom struct that takes the left-hand side and right-hand side as arguments. The struct of course overloads operator+ to take the custom string literal first, returning itself, then the operator+ for the struct itself.
struct contains_struct {
string lhs;
string rhs;
void set_lhs(string lhs) { this->lhs = lhs; }
void set_rhs(string rhs) { this->rhs = rhs; }
operator bool() const {
return string::npos != lhs.find(rhs);
}
} contains_obj;
contains_struct& operator+(const string& lhs, const contains_struct& rhs) {
contains_obj.set_lhs(lhs);
return contains_obj;
}
contains_struct& operator+(const contains_struct& lhs, const string& rhs) {
contains_obj.set_rhs(rhs);
return contains_obj;
}
#define contains +contains_obj+
Now I decided I want to go further. What about
(x in a) perform cube
It's no list comprehension, but it's a pretty good example right? At first I said, well I'd have to go to stackoverflow to ask about custom operator precedence, but it's straight forward to put it in parentheses since no one in their right mind would use my code. Instead, I expanded upon my other example and have 'in' and 'perform' as custom structs, just like 'contains'.
You can go further and template it so that x can be any numerical index, and a as any container, but for simplicity, I left x as an integer and a as a vector of ints. Now so far it doesn't actually take the local variable x as an argument, it uses it locally in the operator string() function.
To simplify things, I store the results of the expression in a string, like so
operator string() const {
string s = "";
for (int x : lhs.rhs)
s += to_string(rhs(x)) + string("\n");
return s;
}
Thanks to another question: Overloading assignment operator for type deduction
I realized one practical use for returning it as an assignment is the following:
struct result_struct {
vector<int> results;
result_struct(vector<int> results) { this->results = results; }
};
...
operator result_struct() const {
vector<int> tmp;
for (int x : lhs.rhs)
tmp.push_back(rhs(x));
return result_struct(tmp);
}
...
result_struct result_2 = (x in a) perform cube;
for (int x : result_2.results)
cout <<x<<endl;
Thanks to milleniumbug's answer, I can do:
struct for_obj
{
int _lhs;
std::vector<int> _rhs;
for_obj(int lhs, std::vector<int> rhs)
: _lhs(lhs), _rhs(rhs) { }
};
INFIX_OPERATOR(for_obj, in_op, int, std::vector<int>)
{
return for_obj(lhs(), rhs());
}
#define in + in_op() +
INFIX_OPERATOR(int, perform_op, for_obj, std::function<int(int)>)
{
for (int i = 0; i < lhs()._rhs.size(); i++)
rhs()(lhs()._rhs[i]);
return 0;
}
#define perform + perform_op() +
There are two caveats. First, I return an int so that I can assign it to a dummy variable to get it to execute. I could always do the result_struct thing I did before, or return a std::function object to call it by itself, but I'd be repeating myself. The other caveat is that because there are so many consts in the macro, you cannot modify the lhs (which doesn't allow you to specifiy an iterator).
All things considered, the following works as expected.
int x = 0;
std::vector<int> nums = { 1, 2, 3 };
auto cube = [] (int x)
{
std::cout << x * x * x << std::endl;
return x * x * x;
};
int i = (x in nums) perform cube;
New version
class PerformObj {
int counter;
public:
PerformObj() : counter(0) { }
~PerformObj() { }
InObj lhs;
std::function<int(int)> rhs;
operator int() const {
return rhs(lhs.rhs[counter]);
}
} performobj;
#define perform + performobj +
PerformObj& operator+(const InObj& lhs, PerformObj& rhs) {
rhs.lhs = lhs;
return rhs;
}
PerformObj& operator+(PerformObj& lhs, const std::function<int(int)>& rhs) {
lhs.rhs = rhs;
return lhs;
}
int main()
{
std::vector<int> nums = {1,2,3};
int x = 0;
auto cube = [] (int n) {
return n * n * n;
};
std::cout << x in nums perform cube << std::endl;
}
explicit operator std::vector<int>() const {
std::vector<int> temp;
for (int i = 0; i < lhs.rhs.size(); i++) {
temp.push_back(rhs(lhs.rhs[i]));
}
return temp;
}
int y = 0;
std::cout << y in static_cast<std::vector<int>>(x in nums perform cube) perform std::function<int(int)>([] (int i) -> int {
return i;
}) << std::endl;
Should I make it so that instead of infix operators, there are postfix operators, like "String literal"s.contains "Other string literal"s, or do it function style, "String literal"s.contains("Other string literal"s)?
How would I improve my code to make it more extensible? As it is right now, it's very polluted. Is there a better/more generalized/less clunky way to do this? For example, to generalize the expressions so that I don't need define statements or to reuse code.
It is hard to see what is the question asked here, assuming the latest edit has all the questions.
Should I make it so that instead of infix operators, there are postfix
operators, like "String literal"s.contains "Other string literal"s, or
do it function style, "String literal"s.contains("Other string
literal"s)?
Yes. "String literal"s.contains("Other string literal"s) is the best way - concise, clear to C++ programmers, clear to programmers of other languages (Java and Python strings have methods) and no template magic nor macro magic is used.
How would I improve my code to make it more extensible? As it is right
now, it's very polluted. Is there a better/more generalized/less
clunky way to do this? For example, to generalize the expressions so
that I don't need define statements or to reuse code.
Yep! But only to certain degree (removed the unnecessary consts over there and here):
#define INFIX_OPERATOR(rettype, name, LT, RT) \
struct name\
{\
private:\
LT* left;\
RT* right;\
\
protected:\
LT& lhs() const { return *left; }\
RT& rhs() const { return *right; }\
\
public: \
friend name operator+(LT& lhs, name && op)\
{\
op.left = &lhs;\
return op;\
}\
\
friend name operator+(name && op, RT& rhs)\
{\
op.right = &rhs;\
return op;\
}\
\
name () : left(nullptr), right(nullptr) {}\
\
operator rettype() const;\
};\
\
inline name :: operator rettype() const
And then you can create your infix operator like this:
#include <iostream>
#include <string>
INFIX_OPERATOR(bool, contains_op, const std::string, const std::string)
{
return std::string::npos != lhs().find(rhs());
}
#define contains + contains_op() +
int main()
{
std::string a = "hello";
std::string b = "hell";
if(a contains b)
std::cout << "YES";
}
Note that there is no way to avoid #define contains directive, as there is no way to create macro directive with another macro directive.
What are the practical benefits of this if there are any (ignoring all
rationality of using this as real world code. I mean what can you get
out of it for what I'm using it for, barring recreational purposes?)
Say that my friend, instead of learning C++, wants an easy abstracted
interface for his Bash or Perl experience but would like to
collaborate without resorting to compiling/linking outside gcc. That
way, he can write 'scripts' or 'code' that is C++, and compile and
link it with my programs/libraries/interface, whatever.
It seems that you are trying to create a language on top of another language. Prepare for
Hours and hours trying to test your language.
Embarrassingly bad diagnostics messages. Try to compile this: std::vector<void> myarr;1 Then wrap it with macros. And then wrap it in another template. And then in another macros... You get the idea.
Debugging tools showing processed code.
Even if your language perfectly integrates with itself, you still have C++ to take care of, with tons of rules and complicated type system. After all, all abstractions are leaky.
If your friend want to program in Perl, just let him do it. These languages are easy to interface with C.
If you're trying to create a language, because the other languages can't cleanly express what you're trying to do, parser generators (Flex/Bison, ANTLR) and LLVM make it easy.
If creating a parser is overkill, take a look at D language mixins. They accept a string created at compile time, and then compile it as if it was inserted directly.
Here...
import std.stdio;
int main()
{
mixin(`write("Hello world");`); //`contents` is a raw string literal
return 0; //so is r"contents"
}
is equivalent to:
import std.stdio;
int main()
{
write("Hello world");
return 0;
}
This is just a simple example. You could have your function that parses a string:
mixin(user1508519s_language(r"(x in a) perform cube"));
1 - Here is how it looks (gcc 4.7.2):
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/bits/stl_construct.h:63:0,
from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:63,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/ext/alloc_traits.h
: In instantiation of 'struct __gnu_cxx::__alloc_traits<std::allocator<void> >':
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
76:28: required from 'struct std::_Vector_base<void, std::allocator<void> >'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
208:11: required from 'class std::vector<void>'
#templateerrors2.cpp:5:19: required from here
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/ext/alloc_traits.h
:189:53: error: no type named 'reference' in 'class std::allocator<void>'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/ext/alloc_traits.h
:190:53: error: no type named 'const_reference' in 'class std::allocator<void>'
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
In instantiation of 'class std::vector<void>':
#templateerrors2.cpp:5:19: required from here
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
292:7: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
467:7: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
684:7: error: invalid parameter type 'std::vector<void>::value_type {aka void}'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
684:7: error: in declaration 'void std::vector<_Tp, _Alloc>::resize(std::vector<
_Tp, _Alloc>::size_type, std::vector<_Tp, _Alloc>::value_type)'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
881:7: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:70:0,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:10
8:5: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
1003:7: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
1179:7: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:70:0,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:21
6:5: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:43
9:5: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:31
6:5: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
In instantiation of 'std::_Vector_base<_Tp, _Alloc>::~_Vector_base() [with _Tp
= void; _Alloc = std::allocator<void>]':
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
247:15: required from 'std::vector<_Tp, _Alloc>::vector() [with _Tp = void; _A
lloc = std::allocator<void>]'
#templateerrors2.cpp:5:19: required from here
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
161:9: error: invalid use of 'void'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
In instantiation of 'void std::_Vector_base<_Tp, _Alloc>::_M_deallocate(std::_V
ector_base<_Tp, _Alloc>::pointer, std::size_t) [with _Tp = void; _Alloc = std::a
llocator<void>; std::_Vector_base<_Tp, _Alloc>::pointer = void*; std::size_t = u
nsigned int]':
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
161:9: required from 'std::_Vector_base<_Tp, _Alloc>::~_Vector_base() [with _T
p = void; _Alloc = std::allocator<void>]'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
247:15: required from 'std::vector<_Tp, _Alloc>::vector() [with _Tp = void; _A
lloc = std::allocator<void>]'
#templateerrors2.cpp:5:19: required from here
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
175:4: error: 'struct std::_Vector_base<void, std::allocator<void> >::_Vector_im
pl' has no member named 'deallocate'
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/bits/stl_algobase.h:66:0,
from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:61,
from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_iterator_
base_types.h: In instantiation of 'struct std::iterator_traits<void*>':
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_construct
.h:127:24: required from 'void std::_Destroy(_ForwardIterator, _ForwardIterato
r) [with _ForwardIterator = void*]'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_construct
.h:155:7: required from 'void std::_Destroy(_ForwardIterator, _ForwardIterator
, std::allocator<_T2>&) [with _ForwardIterator = void*; _Tp = void]'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
403:9: required from 'std::vector<_Tp, _Alloc>::~vector() [with _Tp = void; _A
lloc = std::allocator<void>]'
#templateerrors2.cpp:5:19: required from here
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_iterator_
base_types.h:182:43: error: forming reference to void