|
|
|
|
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Darren New <dne### [at] sanrrcom> wrote:
> Warp wrote:
> > What if you want to call the function (through the pointer) for different
> > objects?
> I can't think of a convenient way offhand to do this in C#. I think here
> you'd likely fall back to a fixed-named function and then subclass objects
> (or inherit from interfaces) to do that.
But the whole idea with function pointers is that you can call different
functions (which have the same signature) from the same code.
In C++ templates have mostly replaced the need for this (or at least made
it way easier in syntax), but not completely.
--
- Warp
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Warp wrote:
> But the whole idea with function pointers is that you can call different
> functions (which have the same signature) from the same code.
You can do that. What you can't do (easily) is have a function pointer to an
instance method that doesn't include the object the instance is invoked on.
In other words, there's no way to declare a function pointer (aka delegate)
that allows you to declare the "this" parameter outside the normal argument
list. So you can't say "xyz.my_delegate(27)". You have to invoke
"my_delegate(xyz, 27)", at which point obviously my_delegate can't point to
a static function that doesn't have a "this" argument. The terrors of
distinguished caller syntax.
You can call any function that has the same signature. If you declare
delegate int xyz(float x);
xyz fun_ptr;
then any variable of type xyz (such as fun_ptr) can accept a pointer to any
function that takes a float as an argument and returns an integer. It
doesn't matter if it's a static function or an instance function, but *if*
it's an instance function, you have to pass in the instance when you create
the pointer, because there's no place else to do it - nowhere in the
declaration of type "xyz" is there a mention of the instance object, so if
it's an instance function, you have to bind the instance before you assign
it to the pointer. I.e., in C++, if you have a pointer to a member
function, you can't invoke it without providing the member when you invoke
it. In C#, if you have a pointer to a function, you have to provide the
member it points to along with the function it points to when you create a
pointer to a member function.
You cannot conveniently (i.e., without code) declare a pointer (or delegate)
that points to an instance function without providing the instance. You
*can* declare a function that takes an instance and arguments and invokes
the appropriate function, then put that in a delegate and pass in which
object you want to use when you invoke it. I.e., you can write a one-liner
that takes "this" from an argument and sticks it before the dot, and then
assign that one-liner to a delegate.
Me, I'm having a hard time imagining a use-case for a pointer to a member
function that *doesn't* carry an object reference with it. I.e., I can't
think of functionality I'd be coding where I know what function I want to
call when I create the pointer, but I don't know what object I want to call
it on. Can you offer an example?
--
Darren New, San Diego CA, USA (PST)
"Ouch ouch ouch!"
"What's wrong? Noodles too hot?"
"No, I have Chopstick Tunnel Syndrome."
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
nemesis wrote:
> Darren New escreveu:
>> Mike Raiford wrote:
>>> That is how C# is now. Sometimes it would be nice just to have one
>>> function with defaults, rather than 3 overloads of the same function
>>> that simply delegate back to the "full featured" function.
>>
>> No, i mean instead of
>> job.print(orient:landscape, copies:2, paper:A4)
>> the suggestion it that it's much more OO to say
>> job.initialize()
>> job.orient(landscape)
>> job.copies(2)
>> job.paper(A4)
>> job.print()
>
> More OO, or more assemblyish? I mean, you individually feed the stack
> and then call a function using those arguments already in the stack. ;)
Some class designers would make most of the member functions in class
PrintJob return a reference to the class so that this could be one line:
job.initialize().orient(landscape).copies(2).paper(A4).print();
Of course, there would also be interminable debate about whether there
should be an initialize() member in any C++ class, and I'm sure that
some would want to override the streaming operator so that it could be
used to feed some of the parameters to the PrintJob class.
Regards,
John
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
John VanSickle wrote:
> Some class designers would make most of the member functions in class
> PrintJob return a reference to the class so that this could be one line:
>
> job.initialize().orient(landscape).copies(2).paper(A4).print();
Yeah. The person I saw advocating this also recommended command/query
separation, so anything that sets a parameter wouldn't be able to return a
value. It would be interesting to know what he'd think of this sort of
chaining, tho, which doesn't seem to violate the spirit of C/Q.
--
Darren New, San Diego CA, USA (PST)
"Ouch ouch ouch!"
"What's wrong? Noodles too hot?"
"No, I have Chopstick Tunnel Syndrome."
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Darren New <dne### [at] sanrrcom> wrote:
> Yeah. The person I saw advocating this also recommended command/query
> separation, so anything that sets a parameter wouldn't be able to return a
> value.
So if setting a parameter to a given value can fail, you can't check
the success/failure of the function call by having the function return
eg. a boolean value informing of this, but instead you have to write a
separate function which you have to call in order to query whether the
operation was successful or not? And this for each such parameter which
can fail?
This presents a multitude of problems. For instance, the class would
have to store booleans for each parameter somewhere, so that the "did
the setting of that parameter succeed?" function can return it.
What if the success query function is called *before* the setting of
the parameter function is called? Can the return value have a meaningful
interpretation anymore?
--
- Warp
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Warp wrote:
> So if setting a parameter to a given value can fail, you can't check
> the success/failure of the function call by having the function return
> eg. a boolean value informing of this, but instead you have to write a
> separate function which you have to call in order to query whether the
> operation was successful or not? And this for each such parameter which
> can fail?
Well, there's two kinds of "setting it failed". One is (for example) setting
a printer to use a certain kind of paper. Here you can't tell before you try
that it's going to fail (assuming it "fails" if that sort of paper isn't in
the printer the moment you set it).
The other kind is (for example) popping the stack on an empty stack, which
you can tell will fail before you even try it. In this case, it's considered
a programming error on the part of the caller to even attempt to pop an
empty stack.
The recommendation that queries and commands be separate is it keeps you
from having to put it in a variable that's unrelated to the object you're
calling. Why would the 'did the paper setting function work' value ever be
stored in the local variables of the function that generates the report,
rather than in the object that represents the printer itself?
It also means that you can have one part of you code intialize the printer
and another part check it's ready to go, without passing printer-specific
values around in code that hasn't anything to do with the printer. It's more
a "big program" sort of technique, along the same lines as "each object
should be responsible for only one thing" sort of thing. The object takes
full responsibility for tracking all its state, and you query the object for
its state every single time you need to know it. If you return status from
mutating calls, you're passing responsibility for tracking the status back
to the caller instead of encapsulating it in the object.
> This presents a multitude of problems. For instance, the class would
> have to store booleans for each parameter somewhere, so that the "did
> the setting of that parameter succeed?" function can return it.
Right. Or, the caller has to do that when they call the function.
The function still has to save that information, because if setting the
paper size failed, you can't print to the printer, right? If you *can*
print to the printer, then it isn't "did the paper setting fail?", but
"after I told you to use A4, what paper size will you use?" It's only a
"failure" if the caller can't accept the paper size the printer's ready to use.
For example: if you set the paper size, the object still has to know what
the paper size is. If you can have a type with a distinguished value (like
NULL in C), you can have the function that returns the paper size return
NULL if the paper size didn't get set correctly, for example.
Look at something like the getchar() function in C. How often do you use
that where you don't have to assign it to a variable to use it? Not very
often. You always wind up writing something like
while ((c = getchar()) != EOF) { do something with c }
Instead, the recommendation is to use the equivalent of
readchar();
while (currentchar() != EOF) {
do something with currentchar()
and something else with currentchar()
readchar();
}
It's a little more verbose, but the "current character" stays with the
object it's associated with. When you start putting distinguished-caller
syntax in the mix, it becomes a lot easier to understand what's going on
when you're in maintenance mode. Like, say you're comparing two lists:
one.initlist();
two.initlist();
while (one.curr() != two.curr()) {
if (one.curr() < two.curr()) one.nextitem();
else if (two.curr() < one.curr()) two.nextitem();
}
If "nextitem" returned the next item of the list, the logic would be
somewhat more convoluted, trying to keep the local variables synchronized
with the contents of the lists, and it would be easy to accidentally assign
two.nextitem() to m_one or something.
> What if the success query function is called *before* the setting of
> the parameter function is called? Can the return value have a meaningful
> interpretation anymore?
I imagine it depends on the function. If it's initialized to a default that
works (like whatever paper size happens to be loaded), then the "will this
work" function would return true. If it's initialized to a value that's
invalid (like the name of the file on a closed file handle) then the "will
this work" function returns false. The constructor has to assure that the
query functions are valid, just like any other initialization the
constructor needs to do.
--
Darren New, San Diego CA, USA (PST)
"Ouch ouch ouch!"
"What's wrong? Noodles too hot?"
"No, I have Chopstick Tunnel Syndrome."
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
|
|