|
|
|
|
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Darren New wrote:
> Warp wrote:
>> Another example: You could give std::sort() a comparator as a lambda
>> function which has access to the variables in the local scope, something
>> which is very laborious to do currently.
>
> Hmmm. OK, thanks. I can see where something like that could be convenient.
> Most places I've seen that need that sort of thing pass in a "client data"
> sort of opaque pointer to allow it, so I guess this new stuff could
> obviate the need to design that in at the application layer.
In C, the standard qsort() function takes a function pointer and (I think)
a "client data" void pointer. You have to put your state in a struct, send
the pointer as client data, then in your comparator function get the data
through the pointer.
In C++, the standard std::sort template function takes a comparator functor.
Using functors (which can be an instance of a stateful class, or can be a
plain old function) instead of passing a "client data" pointer is quite
common in C++.
Here is a [useless in practice] example where the comparator takes an
initialization parameter (whether to sort descending or ascending), and
also keeps state that can be retrieved later (how many times the comparison
function was called).
struct MyComparator {
bool reverse_mode;
int comparison_count;
MyComparator(bool reverse) {
reverse_mode = reverse;
comparison_count = 0;
}
bool operator()(int a, int b) {
comparison_count++;
if (reverse_mode) {
return b < a;
} else {
return a < b;
}
}
};
std::vector<int> list;
MyComparator c(false);
std::sort(list.begin(), list.end(), c);
cout << "Comparator called " << c.comparison_count << " times\n";
If you don't need to retrieve any data after the comparison, you could also
do this:
std::sort(list.begin(), list.end(), MyComparator(false));
With lambdas, you can use "closures" to keep state, so you don't need a
separate class/struct declared elsewhere:
std::vector<int> list;
int comparison_count=0;
std::sort(list.begin(), list.end(), [&comparison_count](int a, int b) {
comparison_count++;
return a < b;
});
Even if you don't want to keep any state (and leave the [] empty to use no
closures), it's still useful to declare the comparison code right next to
the std::sort call, instead of in a function/class possibly far away in
the .cpp file. This is the part I said was similar to string literals (you
know how in assembler you need a manually-made list of strings at the
beginning of the program? :P).
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Nicolas Alvarez <nic### [at] gmailcom> wrote:
> for (std::vector<int>::const_iterator i = someList.begin(); i !=
> someList.end(); ++i) {
> total += *i;
> }
Maybe not the best of examples because with the next C++ standard you
will also be able to do it like:
for(auto i = someList.begin(); i != someList.end(); ++i)
total += *i;
which is not enormously longer than the lambda version.
--
- Warp
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Warp wrote:
> Nicolas Alvarez <nic### [at] gmailcom> wrote:
>> for (std::vector<int>::const_iterator i = someList.begin(); i !=
>> someList.end(); ++i) {
>> total += *i;
>> }
>
> Maybe not the best of examples because with the next C++ standard you
> will also be able to do it like:
>
> for(auto i = someList.begin(); i != someList.end(); ++i)
> total += *i;
>
> which is not enormously longer than the lambda version.
Yes, when changing C++ code to C++0x, I'll have to decide which of the two
types of awesome to use.
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Warp wrote:
> However, it doesn't matter! As long as F behaves like a function, that's
> just fine. This is used quite a lot in template coding, eg. with functors
> and comparators. This gives flexibility and versatility.
Hmm. I never thought of it that way. OK.
> It's not like C++ was the only language which behaves like this.
Well.... I think it's one of the few languages where you can't tell whether
something's invoking a function or adding a name to the namespace at compile
time. It would seem to make writing parsers and such harder than it needs
to be.
I'll grant that there are a number of languages where (say) writing "F(5)"
doesn't tell you whether F is a function, a pointer to a function, or a
function that returns another function, say.
> Most languages have special keywords to denote what you want to do.
> For example, some languages might have a keyword like "function" to denote
> that you are starting a function definition, as opposed to starting a
> function call. I don't really see the big difference.
It makes the grammar easier to automate.
>> You can't tell me a language with undecidable syntax isn't at least a little
>> sucky, yes?
>
> When that syntax brings flexibility, I don't consider is such a huge
> problem.
Other languages seem to manage being as flexible without having such complex
grammars. I'll grant that they trade off runtime efficiency to achieve that.
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Nicolas Alvarez wrote:
> This is the part I said was similar to string literals (you
> know how in assembler you need a manually-made list of strings at the
> beginning of the program? :P).
Oh! OK. I had no idea that's what you were talking about, and I thought you
were comparing lambda expressions to string literals somehow.
Thanks for the excellent examples.
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Darren New <dne### [at] sanrrcom> wrote:
> Well.... I think it's one of the few languages where you can't tell whether
> something's invoking a function or adding a name to the namespace at compile
> time. It would seem to make writing parsers and such harder than it needs
> to be.
Who cares if writing a parser is harder? Programs are supposed to help
people, not the other way around. I prefer latex over xml any day, even
though creating a latex parser is way more complicated than creating an
xml parser.
--
- Warp
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Warp wrote:
> Who cares if writing a parser is harder?
Well, anyone implementing compilers or interpreters for one. If the compiler
gave you runtime type information sufficient for everyone's needs, that
would probably be enough. But if you want to (say) write something that'll
take a structure and store it in database tables, or serialize something to
a file or a SOAP message or some such, not having the type information *and*
not being able to easily parse the header files can be problematic.
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Warp wrote:
> You still know that I get trolled when someone writes such mocking words
> about C++ as you have done.
You know, I took a couple hours away, then went back and looked at what I'd
typed. I think if you look at my initial posts here objectively, you'll see
they weren't mocking, as almost all of it was asking questions about the
implementation.
What I failed to do was describe the mental process that caused me to ask
the kinds of apparently-silly questions I was asking. I thought
(( Wow, C++ is getting lambdas. I guess they're standardizing Boost, which
is ugly. )) "I bet it'll be ugly."
(( No, it actually *looks* not too bad. But I wonder how they implement it,
then. That's a mess behind the scenes in C#, and in pretty much every
language I know that keeps variables on the machine stack, so I wonder how
they're doing closures. I wonder how the closed-over variables survive the
destruction of the stack frame. ))
"Can you return lambda values from a function?" (( Because if not, then you
don't have to worry about the stack frame not being there. Oh, but I'm told
you can. Cool. Maybe it's going to copy the values from the stack into a
heap-allocated object. But how would it GC the heap-allocated object, if
multiple lambdas point to it? Maybe it's doing reference counting. But that
doesn't work, if you get circular references, which you can't get if you
can't assign to a variable except a new variable. ))
"Can you assign it to a variable?" (( Oh, you can do that too. Wow, that's
really impressive. I can't imagine what they're doing to make closures work.))
"Thanks for the link to wikipedia." (( Oh, wikipedia says you just randomly
get hosed if you try to actually use values you thought you closed over when
you created the lambda, unlike every other language that ever implemented
the feature called 'lambda expressions'. ))
"Gee, C++ lambdas sure suck compared to everyone else's that actually
manages to create a closure from lambdas." (( I wonder what it could be
useful for, since I can't think of a reason I'd use a lambda that I couldn't
return from the function in which I created it. ))
"Why not just use a for loop?" (( Oh, I see. I wasn't thinking like that,
because I was still thinking in terms of lambdas rather than in terms of
downward funargs. ))
I can see how if I didn't make clear why I was asking the questions, you
might leap to the conclusion (perhaps justifiably) that I was simply looking
for the flaws in order to criticize C++. In reality, I was looking for the
flaw in my mental model of how C++ handles memory that would allow the
memory for the closure to be collected only after the lambda has been
collected. Or, given that I'm pretty sure I understand how C++ works its
memory model, I was looking for the cool impressive clever mechanism that
would let the closure get allocated in a way that would work.
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Nicolas Alvarez wrote:
> Darren New wrote:
>> I can pass you a void* and
>
> That's a C feature. Real C++ programmers don't use void*. So no, of course
> you have no RTTI on a void*.
OK. What am I doing wrong here, that I'm not getting the size of the actual
instance passed in?
#include <iostream>
class Alpha {
public:
int i;
Alpha() { i = 9; }
virtual ~Alpha() { }
virtual int yidda() { return 0; }
virtual int mysize() { return sizeof(*this); }
};
class Beta : public Alpha {
public:
long l0, l1, l2, l3, l4, l5, l6, l7;
Beta() { i = 10; }
virtual ~Beta() { }
virtual int yidda() { return 1; }
};
void show_size(Alpha & xyz) {
std::cout << sizeof(xyz) << std::endl;
std::cout << xyz.mysize << std::endl;
}
int main() {
Alpha alpha;
Beta beta;
show_size(alpha);
show_size(beta);
return 0;
}
Why does this print "8 8 8 8"? If there's RTTI, shouldn't I at least be able
to figure out how big my structure is without having to code a function to
calculate it for every function? Seriously, if you wanted to write whatever
structure "show_size" got passed out to a file, how would you do it? Would
you have to write a "return sizeof(*this)" in each and every class as a
virtual function? A function inherited by the parent into the child doesn't
know that "this" is pointing to a child type? (I tried adding more longs to
alpha and I started getting 32's, so it's not giving me the size of the
reference itself.)
If I copy the text of mysize() into Beta's class, mysize() returns 40 for
betas. That seems kind of ... not right, to me. I duplicate a virtual
function into a child class without changing it, and it gives different
answers than if I inherited it?
>> The traceback in your exceptions
>> can tell you what the values passed as arguments to the function on each
>> frame...
>
> Yes they can.
I wasn't aware C++ even defined tracebacks on exceptions. How does one
access that feature? The googles with the obvious words mostly talk about
python exceptions passing through C++, and the g++ exception.h file declares
exception as having a constructor, destructor, and a "what" field, but no
traceback. What should I google on to find the right invocation to get the
traceback out of an exception?
>> Hell, you can't even tell me how big an array is
> No, but you'll see me using std::vector, not arrays, in C++.
Yes, because you can't see how big an array is.
>> I know how it *won't* be implemented. It won't be implemented such that
>> references to local variables survive the end of the stack frame those
>> local variables are allocated in. That's enough.
>
> They never do,
Well, no, not in C++. That's kind of my point. In other languages with
lambdas, yes, they do. That's why it's called a closure. That's why I was
asking stupid questions about how C++ manages to do that. :-)
> And in my mind it wouldn't be a stack if it kept
> alive after 'popped' :D
Well, yes. That's exactly why I'm asking "Wow, how does C++ do that??"
Because, like, that's what "lambda" means, and C++ can't do it, so I'm
trying to figure out what amazing mechanism they used.
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Darren New <dne### [at] sanrrcom> wrote:
> OK. What am I doing wrong here, that I'm not getting the size of the actual
> instance passed in?
[snip]
> Why does this print "8 8 8 8"? If there's RTTI, shouldn't I at least be able
> to figure out how big my structure is without having to code a function to
> calculate it for every function?
The problem is that sizeof() is a static construct which is evaluated at
compile time, not something evaluated at runtime. sizeof() in particular
does *not* take into accout RTTI. When you say "sizeof(*this)" (in your
example), that's by standard specification exactly identical to
"sizeof(Alpha)".
But C++ does have RTTI. For example try this:
#include <iostream>
#include <string>
#include <typeinfo>
class Alpha
{
public:
virtual ~Alpha() {}
std::string myType() const { return typeid(*this).name(); }
};
class Beta: public Alpha
{
};
int main()
{
Alpha obj1;
Beta obj2;
Alpha& obj3 = obj2;
std::cout << obj1.myType() << " "
<< obj2.myType() << " "
<< obj3.myType() << std::endl;
}
Even though the class Alpha does not see the class Beta in any way (you
could make completely sure of that by putting them in different compilation
units), it will still return the correct typeid name depending on whether
the object is really of type Alpha or of type Beta.
--
- Warp
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
|
|