Recent

Author Topic: {SOLVED] taking the address of an object method  (Read 4476 times)

Dr.John

  • Newbie
  • Posts: 4
{SOLVED] taking the address of an object method
« on: January 05, 2025, 07:47:21 pm »
This question is related to finding the addresses of methods in objects, but let me first describe what I’m trying to do to give you context.  I’m writing a data logging Lazarus application where there are a number of sensors (in this case, thermocouples) for which data should be logged.  However, the exact number of them is unknown until run time.  The provider of the sensor hardware (Phidgets) usefully provides a library to which you can interface to discover how many sensors there are and connect to them.  Outside of making the initial connections to them, the primary means of communications with them is through events (presumably initiated by hardware) they create.  This necessitates a callback for each event from each sensor.  In this case there are 4 events per sensor (temperature, attached, detached, error), each of which requires its own separate procedure.

I’ve built and currently have running an application that accomplishes this, but it is an excellent example of spaghetti code.  Since I’m most comfortable with the procedural way of thinking about coding software (I reckon from the early Turbo days), outside of using components on forms, I’ve always used Free Pascal in this way.  However, it means that I have a whole slew of callback procedures, each slightly different, that litter my code.  That they are each slightly different (at least in an array index) means that it’s easy to create errors. 

I’ve built the necessary data structures as an array of thermocouple records, so it seems obvious that the next thing that I should do is to create an array of thermocouple objects within which are the callbacks for its particular sensor.  The callbacks only need to access variables within the thermocouple object, so it’s a nice way of segmenting and simplifying the application.  It also lends itself to an implementation where the number of sensors can be discovered at runtime since thermocouple objects can be created and destroyed as needed.

However, the callbacks need to be registered with the library that calls them, i.e., I need to pass the addresses of the procedures that respond to the callbacks to the Phidgets library.  Here is where I have a problem that I haven’t been able to overcome, that is, taking the address of object methods.  I always, in one way or another, reach the same point: an error message that says that it’s illegal to recast an untyped number as a pointer to a procedure.

I’m not sure why I’m receiving this message because I’m reusing code that I used in my original implementation.  I’ve tried other suggested implementations based on other similar questions on this forum, but they all lead to the same results.  I suspect it has to do with the fact that my callback procedures in the original code are static, so their addresses are “known” at compile time, whereas with objects, the addresses of the methods that implement the callbacks may not be known until run time.  It strikes me that it isn’t impossible to find the addresses of the methods at run time, but it may be that such a capability isn’t currently implemented in Lazarus or Free Pascal.

So, my question has a few parts.
1.  Is my understanding of the problem correct?
2.  If not, how should I implement getting addresses of object methods?  If so, is there a known work-around? 
3.  Any recommendations for a different approach that allows me to define the callback procedures only once, although generating the required code for a number of sensors, that number being something that isn’t known at compile time?

Many thanks for your help.

« Last Edit: January 07, 2025, 05:45:41 pm by Dr.John »

cdbc

  • Hero Member
  • *****
  • Posts: 1808
    • http://www.cdbc.dk
Re: taking the address of an object method
« Reply #1 on: January 05, 2025, 08:10:03 pm »
Hi
Code: Pascal  [Select][+][-]
  1. type
  2.   TTest = class
  3.   private
  4.     ...
  5.   public
  6.     class function HandleSensorError(aHandle: ptrint;anErrObj: pointer;anErrObjLen: ptrint): ptrint; static
  7.   end;
  8.  
Then in your registration code, you can do like this:
Code: Pascal  [Select][+][-]
  1.  phi_library_register_callback(SomeHandle,@TTest.HandleSensorError);
  2.  
A 'static' 'class' function / procedure has the attribute, that it behaves like a normal /procedural/ function / procedure, i.e.: there's no 'Self' pointer sent along.
I think this little trick could solve your problem...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

VisualLab

  • Hero Member
  • *****
  • Posts: 624
Re: taking the address of an object method
« Reply #2 on: January 05, 2025, 08:35:44 pm »
On the Phidgets home page, in the main menu, there is a Resources section with a Languages ​​subsection. There is no Pascal language in it, but C is listed first. I assume that you based on a library written in C. But you write that you use Lazarus, so that would mean that:
  • you translated the modules from C to Pascal yourself (or at least some of them) and use DLLs, or
  • you use static linking of OBJ files with your Pascal code.
Nevertheless, you definitely have some header files translated. So the problem exists somewhere in one of these files or in the project settings. Without sample code and feedback about the errors you receive, it is hard to conclude anything. Maybe try to prepare some example posts and attach the content of the errors you receive to it.

TRon

  • Hero Member
  • *****
  • Posts: 3930
Re: taking the address of an object method
« Reply #3 on: January 05, 2025, 09:09:48 pm »
Nevertheless, you definitely have some header files translated. So the problem exists somewhere in one of these files or in the project settings.
User seem to have expressed converting from procedural code to object code. Therefor I am assuming it does already work but user wishes to abstract to objects.

There is a clear difference between a callback routine for a procedure (which the library expects) vs procedure of object (which requires an additional hidden parameter). cdbc's answer solves that (by using a class procedure).

Quote
Without sample code and feedback about the errors you receive, it is hard to conclude anything. Maybe try to prepare some example posts and attach the content of the errors you receive to it.
To that I say +1 because right now it is a theoretical question and from what I've understood atm I would probably solve it another way (but at the same time I am unfamiliar with the API).

To answer OP's question on how to 'obtain' the address of a procedure use addr or @ (but depends on the compile mode)
« Last Edit: January 05, 2025, 09:12:56 pm by TRon »
I do not have to remember anything anymore thanks to total-recall.

VisualLab

  • Hero Member
  • *****
  • Posts: 624
Re: taking the address of an object method
« Reply #4 on: January 05, 2025, 09:57:52 pm »
Nevertheless, you definitely have some header files translated. So the problem exists somewhere in one of these files or in the project settings.
User seem to have expressed converting from procedural code to object code. Therefor I am assuming it does already work but user wishes to abstract to objects.

Yes, I assumed that too.

There is a clear difference between a callback routine for a procedure (which the library expects) vs procedure of object (which requires an additional hidden parameter). cdbc's answer solves that (by using a class procedure).

I wanted to get more information from the OP, because his question is very broad but also very general. Meanwhile, cdbc got ahead of me by providing the solution :)

Moreover, I would separate the translations of C header files (together with callback definitions) from the methods in classes. In my opinion, such code is easier to extend and modify later (of course, everyone does it the way they feel most comfortable).

Dr.John

  • Newbie
  • Posts: 4
Re: taking the address of an object method
« Reply #5 on: January 05, 2025, 10:01:29 pm »
(to cdbc)

!!! BINGO !!!

Well, at least it compiles.  My experience is that, excepting my logic errors, if it compiles, it's likely to run.  Can't test it though today.  It requires a trip to the basement, and it's too damn cold!

Follow-up question:  being a static class means that it's allocating memory on the heap.  Do I have to do anything to manage this, or is it sufficient that it's a member of an object which, itself, is a member of a dynamic array to assure that somehow its instance will be destroyed and the memory returned to the heap?

Many thanks for this nugget!


Dr.John

  • Newbie
  • Posts: 4
Re: taking the address of an object method
« Reply #6 on: January 05, 2025, 10:08:38 pm »
To TRon and VisualLab,

I'm running this code in Ubuntu Linux.  The easiest way to do this is to make an interface to the C library using H2Pas.  The process isn't without its issues, but with a bit of time and effort, it works well.

I deliberately tried to keep the question general because I wanted to focus on what I was trying to do and where the problem existed.  I trusted that, absent an answer like that I received from cdbc, we'd get deeper into specifics, but I didn't think the distraction of specifics was necessary at first.  Apparently cdbc agreed.

Still, many thanks!

VisualLab

  • Hero Member
  • *****
  • Posts: 624
Re: taking the address of an object method
« Reply #7 on: January 05, 2025, 10:18:07 pm »
Follow-up question:  being a static class means that it's allocating memory on the heap.  Do I have to do anything to manage this, or is it sufficient that it's a member of an object which, itself, is a member of a dynamic array to assure that somehow its instance will be destroyed and the memory returned to the heap?

I think this is what you mean: Static class methods.


cdbc

  • Hero Member
  • *****
  • Posts: 1808
    • http://www.cdbc.dk
Re: taking the address of an object method
« Reply #8 on: January 05, 2025, 10:28:05 pm »
Hi
Quote
Follow-up question:  being a static class means that it's allocating memory on the heap.  Do I have to do anything to manage this, or is it sufficient that it's a member of an object which, itself, is a member of a dynamic array to assure that somehow its instance will be destroyed and the memory returned to the heap?
1) The whole class isn't 'static', just the 1 method. In fact, it being a static class procedure/function means, you have NO access to the class to which it belongs, nor the class' innards...
2) Being a class method also means, you can call it like so: TTest.Handlexxx without instantiating the class to an object variable...
I often use this approach together with 'BeginThread', where I send 'Self' in as a param and immediately copies that to a local variable of the right type, then I ONLY work on the local copy...
Funny stuff, that which you're fiddling with  :D
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

jamie

  • Hero Member
  • *****
  • Posts: 6798
Re: taking the address of an object method
« Reply #9 on: January 05, 2025, 10:30:28 pm »
The callback should have an identifier like a handle or device number supplied to your code so you can use a single callback for all devices.

 The trick is to use the identifier to determine the device.

 When setting up the device this same ID should be returned so you can log it and compare to it when the callback triggers, this way you can make a choice of which device it is, and property handle it using a single class.

The callback should be tagged as a  CDECL out side a class.

Jamie

The only true wisdom is knowing you know nothing

TRon

  • Hero Member
  • *****
  • Posts: 3930
Re: taking the address of an object method
« Reply #10 on: January 05, 2025, 10:34:24 pm »
I deliberately tried to keep the question general because I wanted to focus on what I was trying to do and where the problem existed.
Sure but, you have to keep in mind that probably no one here is familiar with the API. If there is someone who is then you're extremely lucky (the chance that someone else is using the exact same hardware such as yours and uses pascal can be a bit problematic).

Quote
I trusted that, absent an answer like that I received from cdbc, we'd get deeper into specifics, but I didn't think the distraction of specifics was necessary at first.  Apparently cdbc agreed.
cdbc offered an answer you are able to work with. It does not necessarily means it is the right solution for your use case. Note that static class procedures are actually static and thus the same for every derived object (and that might perhaps not fit your use case)

Also note that the declaration has to match the c-callback header (including the calling convention)

Quote
Still, many thanks!
You're most welcome. Would be nice to know if in the end cdbc's answer suited your use case.

The callback should have an identifier like a handle or device number supplied to your code so you can use a single callback for all devices.

The trick is to use the identifier to determine the device.
Depends on how TS implements his solution. Like VisualLab I would probably opt for another approach exactly because of what you mentioned
« Last Edit: January 05, 2025, 10:39:24 pm by TRon »
I do not have to remember anything anymore thanks to total-recall.

Dr.John

  • Newbie
  • Posts: 4
Re: taking the address of an object method
« Reply #11 on: January 07, 2025, 01:22:43 am »
OK folks!  First, please accept my apologies for the late response, but, it's been cold outside, which means that the furnace is running hard, and that means that the basement is almost as cold as it is outside, a major disincentive to spend time down there.  But, I have results.

Turns out that TRon is correct.  The static method proposed by cdbc works, but doesn't.  It seems that it doesn't work in the sense that being a single instance, it doesn't place the required data in the correct object.  Hence, a creating a one to one mapping of a callback handler to an object by this technique doesn't work.  At least, I couldn't make it work.

Jamie's hint that I should use the handle as a means to determine which data set I'm modifying is a good one.  I was able to make this work, and did so without the complication of an object, although it adds a bit of code to the callback procedure to figure out which data set corresponds to the handle that's returned.  It's a fair trade-off for reducing the amount of code I have to manage.

It still leaves me wondering about my original question though: is there no way to find the address of a method of an object?

I asked for both an answer to this question (yes, it's a bit abstract), as well as for a work-around.  I got the latter.  I'm satisfied.

Many thanks again for your help.

(Do I need to mark this problem as solved?  If so, how do I do that?)





cdbc

  • Hero Member
  • *****
  • Posts: 1808
    • http://www.cdbc.dk
Re: taking the address of an object method
« Reply #12 on: January 07, 2025, 09:08:33 am »
Hi
I'm glad, you got it to work  :)
The method-address is tucked away safely inside the object's VMT, where it's the 'code' part of a 'TMethod', how to pick it out and actually use it 'in the wild' has to come from someone at a higher pay-grade than mine  ;)
Quote
(Do I need to mark this problem as solved?  If so, how do I do that?)
Yes, that would be nice... You open the first post in this thread and modify that header, then click on the [Save] button, easy peasy  :D
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

egsuh

  • Hero Member
  • *****
  • Posts: 1529
Re: taking the address of an object method
« Reply #13 on: January 07, 2025, 09:53:00 am »
Quote
is there no way to find the address of a method of an object?

I think you can use procedural types. Here's full explanation and examples.

https://www.freepascal.org/docs-html/ref/refse17.html


TRon

  • Hero Member
  • *****
  • Posts: 3930
Re: taking the address of an object method
« Reply #14 on: January 07, 2025, 02:33:10 pm »
I asked for both an answer to this question (yes, it's a bit abstract), as well as for a work-around.  I got the latter.  I'm satisfied.
Thank you for reporting back dr.John.

As you explained/discovered and unfortunately the class procedure approach has its drawbacks.

If you are happy with the current implementation then I am happy for you that you got things solved.

I do not know how time critical these events are but my initial thought was to separate the objects from the events and create a (generic) event dispatcher/handler (can be an object using a class method or multiple class methods as long as it is created/used once). The dispatcher would allow for individual objects to be added (and removed once the object is freed) to the dispatcher and store everything into a list/table (device handle, object etc). The event dispatcher would add itself to the individual event listeners of the library using a single event handler, using device handle to lookup the object in the list by using that handle and initiate the individual's objects event.

That way your are still able to use the object events while at the same time being able to use the library's procedural event handlers. But I stress again not being familiar with the library so this might perhaps not be an option at all.
I do not have to remember anything anymore thanks to total-recall.

 

TinyPortal © 2005-2018