If you pass a record pointer, then this problem is easy to solve, even in Delphi versions that don't support nested record types. Forward-declare a record pointer type, and then declare the function type using the record pointer. Finally, declare the record:
type
PDisassembler_info = ^TDisassembler_info;
TPrint_address_func = function(offset: Integer;
info: PDisassembler_info): Boolean;
TDisassembler_info = record
data: string;
print_address_func: TPrint_address_func;
end;
You're probably going to have more than just one function pointer, and you're probably going to have more than one instance of your record, too. As you extend this pattern, you're ultimately going to re-invent classes. Consider this:
type
TDisassembler_info = class
data: string;
function print_address(offset: Integer): Boolean; virtual; abstract;
end;
Now, instead of defining a free function, you declare a descendant of your class and override the abstract method. This has a few advantages as the number of function pointers and record instances grows:
The compiler automatically fills in the function pointers with all the right values. It stores them in the class's VMT. There's no chance you'll have a null function pointer by accidentally forgetting to assign print_address_func
. The compiler will warn if you attempt to instantiate a class without overriding the abstract methods.
It's impossible to accidentally pass the wrong record pointer when you call the function. In your design, calling the function will look like this:
info.print_address_func(offset, info);
It would surely be an error if the record parameter you passed differed from the record whose function you called. With an object, the redundancy and opportunity for error go away:
info.print_address(offset);
No matter how many functions you have, the size of a single instance of the class remains constant because all instances share a single VMT. In your current model, if you have 100 instances of your record, you'll have 100 copies of the same function pointer.