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
Related
I have some pretty simple C++ code that creates points from a struct definition and tries to add those points to a set.
#include <stdio.h> /* printf */
#include <bits/stdc++.h> /* vector of strings */
using namespace std;
struct point
{
int x;
int y;
};
int main(){
for(int i = 0; i <= 6; i++){
set<point> visited_points;
point visited_point{4, 1};
visited_points.insert(visited_point);
}
}
But this code throws a large console error when I run it saying:
In file included from /usr/include/c++/7/string:48:0,
from /usr/include/c++/7/bits/locale_classes.h:40,
from /usr/include/c++/7/bits/ios_base.h:41,
from /usr/include/c++/7/ios:42,
from /usr/include/c++/7/istream:38,
from /usr/include/c++/7/sstream:38,
from /usr/include/c++/7/complex:45,
from /usr/include/c++/7/ccomplex:39,
from /usr/include/x86_64-linux-gnu/c++/7/bits/stdc++.h:52,
from ex.cpp:2:
/usr/include/c++/7/bits/stl_function.h: In instantiation of ‘constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = point]’:
/usr/include/c++/7/bits/stl_tree.h:2038:11: required from ‘std::pair<std::_Rb_tree_node_base*, std::
........
/usr/include/c++/7/bits/stl_function.h:386:20: note: ‘const point’ is not derived from ‘const std::__cxx11::sub_match<_BiIter>’
{ return __x < __y; }
~~~~^~~~~
Is there a part of my code that I did wrong? I just want a way to keep track of multiple points in a list.
Sets are ordered, and so the elements need an ordering function. Your point class doesn't not have this. Add a suitable definition of
bool operator<(const Point& a, const Point& b);
For instance
bool operator<(const Point& a, const Point& b)
{
return a.x < b.x || a.x == b.x && a.y < b.y;
}
But whatever ordering function you choose it must define a strict weak ordering
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.
So I'm currently developping a so and a dll file to handle the files of my users (basically a filesystem).
I have two classes, one is FileData (representing a single file), and a FileList (which as the name suggests is a list of FileDatas). Here is my problem. My code was working, but I needed to implement a sort method based on the metadatas I get on my file and to do that I needed to get my getter to a const method :
I'm compiling with the flags -Wextra -Wall -Werror
FileData.hpp
class FileData
{
// ...
std::vector<std::pair<std::string, std::string> > _metadatas;
public:
FileData(std::string);
~FileData();
FileData(const FileData &);
std::vector<std::pair<std::string, std::string> > &getMetadatas() const;
};
FileData.cpp (i tried to both return (_metadatas) and (&_metadatas) both fail)
std::vector<std::pair<std::string, std::string> > &FileData::getMetadatas() const
{
return (_metadatas);
}
And here is the error message :
FileData.cpp: In member function ‘std::vector<std::pair<std::basic_string<char>, std::basic_string<char> > >& FileData::getMetadatas() const’:
FileData.cpp:164:23: error: invalid initialization of reference of type ‘std::vector<std::pair<std::basic_string<char>, std::basic_string<char> > >&’ from expression of type ‘const std::vector<std::pair<std::basic_string<char>, std::basic_string<char> > >’
return (_metadatas);
^
make: *** [FileData.o] Error 1
And here is the reason in my FileList.cpp i need to get the getMetadatas a const getter (even though i know getters should always be const anyway) :
FileList.cpp
bool SortMetadata::sortArtist(const FileData &a, const FileData &b)
{
int i,j;
std::vector<std::pair<std::string, std::string> > tmp, temp;
tmp = a.getMetadatas();
temp = b.getMetadatas();
std::string first("");
std::string second("");
for (i = 0; i < tmp.size(); ++i)
{
if (tmp[i].first.compare("artist") == 0)
first = tmp[i].second;
}
for (j = 0; j < temp.size(); ++j)
{
if (temp[j].first.compare("artist") == 0)
second = temp[j].second;
}
return (first.compare(second) < 0);
}
Here is the error message i get if i don't make my getMetadatas() a const method :
FileList.cpp: In function ‘bool SortMetadata::sortArtist(const FileData&, const FileData&)’:
FileList.cpp:91:26: error: passing ‘const FileData’ as ‘this’ argument of ‘std::vector<std::pair<std::basic_string<char>, std::basic_string<char> > >& FileData::getMetadatas()’ discards qualifiers [-fpermissive]
tmp = a.getMetadatas();
^
FileList.cpp:92:27: error: passing ‘const FileData’ as ‘this’ argument of ‘std::vector<std::pair<std::basic_string<char>, std::basic_string<char> > >& FileData::getMetadatas()’ discards qualifiers [-fpermissive]
temp = b.getMetadatas();
I'm not sure what's the problem here, since the code was working fine before I changed my getter to const, and I don't understand what it changes to be const, since the method is just a return..
Thanks for the help guys !
You cannot return a reference to non-const member from a const getter1. You can have two overloads though (const and non-const):
std::vector<std::pair<std::string, std::string> > & getMetadatas();
std::vector<std::pair<std::string, std::string> > const& getMetadatas() const;
But I think you don't even want the first overload and you just forgot one const in the return type.
As #M.M noticed, you don't use address-of operator when returning a reference:
return _metadatas;
(1) It's because you'd violate const-correctness with the following code if that was possible:
const FileData fd;
fd.getMetadatas() = {}; // you're modifying a const object
I have a simple wrapper around C null-terminated string, which is essentially a subclass of std::vector< char >. (Yes, I know about std::string, but my wrapper is easier to cooperate with C functions expecting char*. Also, std::string isn't guaranteed to be contiguous in C++03)
Here is the code:
#include <cstdio>
#include <vector>
typedef std::vector<char> vector_char;
class c_string : public vector_char
{
public:
c_string(size_t size) : vector_char(size+1) {}
c_string(const char* str)
{
if(!str) return;
const char* iter = str;
do
this->push_back(*iter);
while(*iter++);
}
c_string() {}
//c_string(std::nullptr_t) {}
char* data()
{
if(this->size())
return &((*this)[0]); //line 26
else
return 0;
}
const char* data() const { return this->data(); }
operator char*() { return this->data(); }
operator const char*() const { return this->data(); }
};
int main()
{
c_string first("Hello world");
c_string second(1024);
printf("%s",first.data());
printf("%c\n",first[0]);
snprintf(second, second.size(), "%d %d %d", 5353, 22, 777);
printf(second);
}
MinGW complains about:
D:\prog\PROJEKTYCPP\hehe_testc_cpp.cpp: In member function 'char* c_string::data()':
D:\prog\PROJEKTYCPP\hehe_testc_cpp.cpp:26:22: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [enabled by default]
In file included from d:\prog\mingw\bin\../lib/gcc/mingw32/4.7.0/include/c++/vector:65:0,
from D:\prog\PROJEKTYCPP\hehe_testc_cpp.cpp:2:
d:\prog\mingw\bin\../lib/gcc/mingw32/4.7.0/include/c++/bits/stl_vector.h:768:7:note: candidate 1: std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[]:(std::vector<_Tp, _Alloc>::size_type) [with _Tp = char; _Alloc = std:
:allocator<char>; std::vector<_Tp, _Alloc>::reference = char&; std::vector<_Tp,_Alloc>::size_type = unsigned int]
D:\prog\PROJEKTYCPP\hehe_testc_cpp.cpp:26:22: note: candidate 2: operator[](char
*, int) <built-in>
How can I enforce calling correct overload? Can this problem hurt me silently?
By having an operator char * you've provided two ways to do operator[]. The first is std::vector::operator[] applied directly; the second is to convert this to char* and apply [] to that. In this case they both result in the same thing, but the compiler can't know that.
Resolve it by specifying explicitly which one you want.
return &(operator[](0)); //line 26
or
return &((char*)(*this)[0]); //line 26
To delete the first warning, you can do this:
char* data()
{
if(this->size())
return &vector_char::operator[](0);
else
return 0;
}
To delete all warnings, remove the operator char*() and operator const char*() members.
I am trying to make a class that wraps std::map and does checking to make sure the keys are one the of approved valid strings, and also initializes the map to have default values for all the approved valid strings. I am having issues getting the subscript operator to work, specifically the const version of it.
Here is my class prototyping code:
#include <set>
#include <string>
#include <map>
class foo {
public:
foo() {}
const double & operator[](const std::string key) const {
return data[key];
}
private:
static const std::set<std::string> validkeys;
std::map<std::string, double> data;
};
const std::set<std::string> foo::validkeys = {"foo1", "foo2"};
When I compile this (using g++ with -std=c++0x), I get this compilation error:
|| /home/luke/tmp/testmap.cc: In member function 'double& foo::operator[](std::string) const':
testmap.cc|10 col 22 error| passing 'const std::map<std::basic_string<char>, double>' as
'this' argument of 'mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const
key_type&) [with _Key = std::basic_string<char>, _Tp = double, _Compare =
std::less<std::basic_string<char> >, _Alloc = std::allocator<std::pair<const
std::basic_string<char>, double> >, mapped_type = double, key_type =
std::basic_string<char>]' discards qualifiers
Nothing I do seems to fix this. I have tried
making validkeys a std::set and data std::map
using const char * instead of string
returning const double or double instead of const double &
using list and vector instead of set to store the validkeys
I don't know if I'm even approaching this problem correctly so if there is some other simple way to create a class that allows this kind of functionality:
foo a;
a["foo2"] = a["foo1"] = 5.0;
// This would raise a std::runtime_error because I would be checking that
// "foo3" isn't in validkeys
a["foo3"] = 4.0;
Any suggestions greatly appreciated.
SOLUTION
The following works exactly how I want it to, I even have a basic exception when you try to set or get a key that isn't in the set of valid keys:
#include <iostream>
#include <string>
#include <map>
#include <set>
#include <stdexcept>
class myfooexception : public std::runtime_error
{
public:
myfooexception(const std::string & s)
: std::runtime_error(s + " is not a valid key.") {}
};
class foo {
public:
foo() {
for (std::set<std::string>::iterator it = validkeys.begin();
it != validkeys.end();
++it) {
data[*it] = 0.0;
}
}
const double & operator[](const std::string & key) const {
if (data.find(key) == data.end()) {
throw myfooexception(key);
} else {
return data.find(key)->second;
}
}
double & operator[](const std::string & key) {
if (data.find(key) == data.end()) {
throw myfooexception(key);
} else {
return data[key];
}
}
private:
static const std::set<std::string> validkeys;
std::map<std::string, double> data;
};
const std::set<std::string> foo::validkeys = {"foo1", "foo2"};
int main(void)
{
foo a;
a["foo1"] = 2.0;
a["foo1"] = a["foo2"] = 1.5;
// a["foo3"] = 2.3; // raises exception: foo3 is is not a valid key
const foo b;
std::cout << b["foo1"]; // should be ok
// b["foo1"] = 5.0; // compliation error, as expected: b is const.
return 0;
}
The operator [] is not declared const in the std::map, because the operator [] also inserts a new element when the key is not found and returns a reference to its mapped value. You can use the map::find method instead of map::operator[] if you want your operator[] to be const.
The subscript operator for std::map is non-const as it inserts a new element if one does not yet exist. If you want your map to have a const operator[], you need to write one that uses map::find() and tests against map::end(), handling the error case.
you are trying to modify a const object!!
please remove the const of set.const members cannot be modified once they are initialised.
You are trying to assign to the std::map but your function is declared const and also returning const. Remove both const and it should work.