Rocksolid Light

Welcome to novaBBS (click a section below)

mail  files  register  newsreader  groups  login

Message-ID:  

I do not fear computers. I fear the lack of them. -- Isaac Asimov


devel / comp.arch.embedded / Re: Function pointers: good or bad things?

SubjectAuthor
* Function pointers: good or bad things?pozz
+* Re: Function pointers: good or bad things?David Brown
|`* Re: Function pointers: good or bad things?pozz
| `- Re: Function pointers: good or bad things?David Brown
`* Re: Function pointers: good or bad things?Don Y
 `* Re: Function pointers: good or bad things?David Brown
  `* Re: Function pointers: good or bad things?Don Y
   `* Re: Function pointers: good or bad things?David Brown
    `- Re: Function pointers: good or bad things?Don Y

1
Function pointers: good or bad things?

<sn512d$lbo$1@dont-email.me>

 copy mid

https://www.novabbs.com/devel/article-flat.php?id=721&group=comp.arch.embedded#721

 copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: pozzu...@gmail.com (pozz)
Newsgroups: comp.arch.embedded
Subject: Function pointers: good or bad things?
Date: Thu, 18 Nov 2021 08:58:05 +0100
Organization: A noiseless patient Spider
Lines: 52
Message-ID: <sn512d$lbo$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Thu, 18 Nov 2021 07:58:05 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="3d829fb3e17ff69060ac30b30f2cf54e";
logging-data="21880"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/BW/8gAlxKrxdjks6cv0CUXQgGbv9BOPA="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101
Thunderbird/91.3.1
Cancel-Lock: sha1:JY7oyaGUG0FiK3zsPnt+dWkJAmY=
 by: pozz - Thu, 18 Nov 2021 07:58 UTC

In reply to my previous post, Paul Rubin says:

"I think MISRA C disallows function pointers, partly for this reason."

Are function pointers really bad things?

Sincerely I found them useful and lastly I was using them more and more.
Am I wrong?

Function pointers help me in at least three situation.

With function pointer I can isolate C modules from the rest of the
project, so it is much more simple to create a well-define hardware
abstraction layer that let me improve portability to other hw platforms,
but mostly let me create a "simulator" on the development machine.

For example, consider a simple leds module interface:

int leds_init(void (*on_fn)(uint8_t idx), void (*off_fn)(uint8_t idx));
int led_on(uint8_t led_idx);
int led_off(uint8_t led_idx);
int led_toggle(uint8_t led_idx);
int led_blink(uint8_t led_idx);
int leds_on_all(void);
...

Here the important function is leds_init() with function pointers
arguments that really switch on or off the LED idx. I call leds_init()
in this way:

leds_init(bsp_led_on, bsp_led_off);

On target bsp_led_on() manages GPIO registers or SPI GPIO expander and
so on, on development machine bsp_led_on() could be a printf() or a
different icon on a GUI.

In this context, functions pointers help to have testability modules. If
I want to test leds module, the test code could call leds_init() in this
way:

leds_init(test_led_on, test_led_off);

So during tests it's much simpler to break the links between modules and
insert the test suite where it is necessary.

Another nice thing that is possible with function pointers is to make
some OOP tricks, for example polymorfism.

Of course one drawback of using function pointers is stack usage
calculation (see my previous post): it would be impossible to make a
calc, because the tool can't resolve the function pointer call.

Re: Function pointers: good or bad things?

<sn56sl$om1$1@dont-email.me>

 copy mid

https://www.novabbs.com/devel/article-flat.php?id=723&group=comp.arch.embedded#723

 copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: david.br...@hesbynett.no (David Brown)
Newsgroups: comp.arch.embedded
Subject: Re: Function pointers: good or bad things?
Date: Thu, 18 Nov 2021 10:37:24 +0100
Organization: A noiseless patient Spider
Lines: 101
Message-ID: <sn56sl$om1$1@dont-email.me>
References: <sn512d$lbo$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Injection-Date: Thu, 18 Nov 2021 09:37:25 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="eacba1fa919d2d9944baf4e35984ee78";
logging-data="25281"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+EfCKZj2h/Ba/RWgL4azXfL8+m2hBcn/Q="
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101
Thunderbird/78.11.0
Cancel-Lock: sha1:n0Zbmpdhry6yNU8ZIBiawicilpA=
In-Reply-To: <sn512d$lbo$1@dont-email.me>
Content-Language: en-GB
 by: David Brown - Thu, 18 Nov 2021 09:37 UTC

On 18/11/2021 08:58, pozz wrote:
> In reply to my previous post, Paul Rubin says:
>
>   "I think MISRA C disallows function pointers, partly for this reason."
>

He is, AFAIK, wrong - but I only looked at one version of MISRA (MISRA C
2012).

> Are function pointers really bad things?
>

Yes.

> Sincerely I found them useful and lastly I was using them more and more.
> Am I wrong?

No.

They are useful, but they are also bad !

>
> Function pointers help me in at least three situation.
>
> With function pointer I can isolate C modules from the rest of the
> project, so it is much more simple to create a well-define hardware
> abstraction layer that let me improve portability to other hw platforms,
> but mostly let me create a "simulator" on the development machine.
>
> For example, consider a simple leds module interface:
>
>   int leds_init(void (*on_fn)(uint8_t idx), void (*off_fn)(uint8_t idx));
>   int led_on(uint8_t led_idx);
>   int led_off(uint8_t led_idx);
>   int led_toggle(uint8_t led_idx);
>   int led_blink(uint8_t led_idx);
>   int leds_on_all(void);
>   ...
>
> Here the important function is leds_init() with function pointers
> arguments that really switch on or off the LED idx. I call leds_init()
> in this way:
>
>   leds_init(bsp_led_on, bsp_led_off);
>
> On target bsp_led_on() manages GPIO registers or SPI GPIO expander and
> so on, on development machine bsp_led_on() could be a printf() or a
> different icon on a GUI.
>
> In this context, functions pointers help to have testability modules. If
> I want to test leds module, the test code could call leds_init() in this
> way:
>
>   leds_init(test_led_on, test_led_off);
>
> So during tests it's much simpler to break the links between modules and
> insert the test suite where it is necessary.
>

There is no doubt that function pointers are useful for this kind of
thing. But really, what you describe here is crying out for a move to
C++ and to use an Led class with virtual functions. While that may seem
like just function pointers underneath, they are /much/ safer because
they are tied so tightly to specific types and uses.

One alternative is to have a "proxy" in the middle that routes between
the modules. Another is to have connections handled via a header,
perhaps with conditional compilation.

Think of this as analogous to electronics. Function pointers are like
free connectors on a board that let you re-wire the board when you use
it. That can be very flexible, but makes it very difficult to be sure
the board is working and can quickly give you spaghetti systems. C++
virtual functions are like connectors with unique shapes - you can make
a few choices of your connections, but only to those points you have
specifically allowed. A "proxy" module is like a multiplexer or buffer
for controlling the signal routing, and a header with conditional
compilation is like DIP switches or jumpers.

Remember, the strength of a programming technique is often best measured
in terms of what it /restricts/ you from doing, not from what it
/allows/ you to do. It is more important that it is hard to get things
wrong, than to make it easy to get things right.

>
> Another nice thing that is possible with function pointers is to make
> some OOP tricks, for example polymorfism.

Don't go there. Go to C++, rather than a half-baked home-made solution
in C. Using C for OOP like this made sense in the old days - but not now.

>
> Of course one drawback of using function pointers is stack usage
> calculation (see my previous post): it would be impossible to make a
> calc, because the tool can't resolve the function pointer call.

That is one drawback. In general, function pointers means you can't
make a call-tree from your program. (Embedded systems generally have a
"call forest", since interrupts start their own trees, as do threads or
tasks.) You can't follow the logic of the program, either with tools or
manually.

Re: Function pointers: good or bad things?

<sn6lrg$dqm$1@dont-email.me>

 copy mid

https://www.novabbs.com/devel/article-flat.php?id=724&group=comp.arch.embedded#724

 copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: pozzu...@gmail.com (pozz)
Newsgroups: comp.arch.embedded
Subject: Re: Function pointers: good or bad things?
Date: Thu, 18 Nov 2021 23:58:56 +0100
Organization: A noiseless patient Spider
Lines: 170
Message-ID: <sn6lrg$dqm$1@dont-email.me>
References: <sn512d$lbo$1@dont-email.me> <sn56sl$om1$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Thu, 18 Nov 2021 22:58:57 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="009ee7c07a5b499d281dbbfc68cd3436";
logging-data="14166"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/jKjBNXNcnhK1gzvSbfW+aKIunLPrRWIo="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101
Thunderbird/91.3.1
Cancel-Lock: sha1:ss8zsV2YKrdHHp0zOKoAgUH+1XI=
In-Reply-To: <sn56sl$om1$1@dont-email.me>
 by: pozz - Thu, 18 Nov 2021 22:58 UTC

Il 18/11/2021 10:37, David Brown ha scritto:
> On 18/11/2021 08:58, pozz wrote:
[...]
> One alternative is to have a "proxy" in the middle that routes between
> the modules. Another is to have connections handled via a header,
> perhaps with conditional compilation.

It should be nice to have a few examples of these approaches.

> Think of this as analogous to electronics. Function pointers are like
> free connectors on a board that let you re-wire the board when you use
> it. That can be very flexible, but makes it very difficult to be sure
> the board is working and can quickly give you spaghetti systems. C++
> virtual functions are like connectors with unique shapes - you can make
> a few choices of your connections, but only to those points you have
> specifically allowed. A "proxy" module is like a multiplexer or buffer
> for controlling the signal routing, and a header with conditional
> compilation is like DIP switches or jumpers.
>
> Remember, the strength of a programming technique is often best measured
> in terms of what it /restricts/ you from doing, not from what it
> /allows/ you to do. It is more important that it is hard to get things
> wrong, than to make it easy to get things right.
>
>>
>> Another nice thing that is possible with function pointers is to make
>> some OOP tricks, for example polymorfism.
>
> Don't go there. Go to C++, rather than a half-baked home-made solution
> in C. Using C for OOP like this made sense in the old days - but not now.
>
>>
>> Of course one drawback of using function pointers is stack usage
>> calculation (see my previous post): it would be impossible to make a
>> calc, because the tool can't resolve the function pointer call.
>
> That is one drawback. In general, function pointers means you can't
> make a call-tree from your program. (Embedded systems generally have a
> "call forest", since interrupts start their own trees, as do threads or
> tasks.) You can't follow the logic of the program, either with tools or
> manually.

However function pointers usually could assume only a few and fixed
values, mostly only *one* value in a build (for example, bsp_led_on()
and bsp_led_off() for my previous example).
I think that if it's possible to inform the call-graph or stack usage
tool of these "connections", it would be possible to generate a good
call graph and worst case stack usage.

Another situation where I use function pointers is when I have a
callback. Just to describe an example, you have two modules: the lower
level module [battery] that monitors continuously the battery level and
emit an event (call a function) when the level goes under a custom
threshold.
Suppose this event must be acquired by [control] module. One solution
without function pointers could be:

/* battery.h */
int battery_set_low_thres(uint16_t lvl_mV);

/* battery.c */
#include "battery.h"
#include "control.h"
....
if (current_lvl < low_thres) {
control_battery_low_level_event(current_lvl);
}
...

/* control.h */
void control_battery_low_level_event(uint16_t lvl_mV);

/* control.c */
#include "control.h"
#include "battery.h"

void control_battery_low_level_event(uint16_t lvl_mV) {
bsp_led_on(BSP_LED_BATTERY_LOW);
power_down();
}

I don't like it, the lower level module [battery] is stricly coupled to
[control] module, because it needs control_battery_low_level_event()
function declaration. Indeed "control.h" must be included in battery.c.

Instead I like an approach that uses a function pointer:

/* battery.h */
typedef void (*battery_low_cb)(uint16_t lvl_mV);
int battery_set_low_thres(uint16_t lvl_mV);

/* battery.c */
#include "battery.h"
....
static battery_low_cb low_cb;
....
if (current_lvl < low_thres) {
if (low_cb != NULL) low_cb(current_lvl);
}
...

/* control.h */
void control_battery_low_level_event(uint16_t lvl_mV);

/* control.c */
#include "control.h"
#include "battery.h"

static void control_battery_low_level_event(uint16_t lvl_mV);

void control_init(void) {
battery_set_low_thres(2700, control_battery_low_level_event);
}

static void control_battery_low_level_event(uint16_t lvl_mV) {
bsp_led_on(BSP_LED_BATTERY_LOW);
power_down();
}

Here the lower level module [battery] doesn't know anything about the
higher level module [control], indeed "control.h" isn't included at all
in battery.c.

It appears to me this approach is perfectly legal and much simpler to
test because of less linking between modules.
Because [battery] doesn't depend on other higher level modules, I can
reuse it in other projects as is.

Moreover there are many C projects, even for embedded, that makes large
use of this approach. For example, many functions in lwip project accept
function pointers to callback (for example, tcp_connect()[1]).

Another approach could be using weak functions:

/* battery.h */
int battery_set_low_thres(uint16_t lvl_mV);

/* battery.c */
#include "battery.h"
....
void battery_event_low(uint16_t lvl_mV) __attrinute((weak));
void battery_event_low(uint16_t lvl_mV) {
UNUSED(lvl_mV);
}
....
if (current_lvl < low_thres) {
if (low_cb != NULL) battery_event_low(current_lvl);
}
...

/* control.h */
void control_battery_low_level_event(uint16_t lvl_mV);

/* control.c */
#include "control.h"
#include "battery.h"

void control_init(void) {
battery_set_low_thres(2700, control_battery_low_level_event);
}

void battery_event_low(uint16_t lvl_mV) {
bsp_led_on(BSP_LED_BATTERY_LOW);
power_down();
}

[1] https://www.nongnu.org/lwip/2_1_x/group__tcp__raw.html

Re: Function pointers: good or bad things?

<sn8irf$be9$1@dont-email.me>

 copy mid

https://www.novabbs.com/devel/article-flat.php?id=725&group=comp.arch.embedded#725

 copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: david.br...@hesbynett.no (David Brown)
Newsgroups: comp.arch.embedded
Subject: Re: Function pointers: good or bad things?
Date: Fri, 19 Nov 2021 17:19:58 +0100
Organization: A noiseless patient Spider
Lines: 231
Message-ID: <sn8irf$be9$1@dont-email.me>
References: <sn512d$lbo$1@dont-email.me> <sn56sl$om1$1@dont-email.me>
<sn6lrg$dqm$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Injection-Date: Fri, 19 Nov 2021 16:19:59 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="25454b895071ee4c0be7dbdd230648d0";
logging-data="11721"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/ZmMei//yX7Yu1J3LvT2sqwWiZpbkSb9k="
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101
Thunderbird/78.11.0
Cancel-Lock: sha1:9LCVeLhpfVr6aSqSxHdnU9NgNRE=
In-Reply-To: <sn6lrg$dqm$1@dont-email.me>
Content-Language: en-GB
 by: David Brown - Fri, 19 Nov 2021 16:19 UTC

On 18/11/2021 23:58, pozz wrote:
> Il 18/11/2021 10:37, David Brown ha scritto:
>> On 18/11/2021 08:58, pozz wrote:
> [...]
>> One alternative is to have a "proxy" in the middle that routes between
>> the modules.  Another is to have connections handled via a header,
>> perhaps with conditional compilation.
>
> It should be nice to have a few examples of these approaches.

The rough idea is that you want to have something like these modules :

blinker (handling timing and the "user interface" of an led)
gpio (handling locally connected pins)
spi (handling pins connected via an spi bus).

You want the modules to be basically independent. You should be able to
write the blinker module without knowing whether the actual led is
connected directly to the microcontroller, or via an SPI bus. You
should be able to write the gpio and spi modules without knowing what
the pins will be used for.

Then you have a "master" module that somehow joins things together.

You have been using function pointers - in "master", you call a function
in "blinker" with pointers to functions in "gpio" or "spi".

Alternatively, blinker could call fixed named functions "blink_on" and
"blink_off" that are expected to be defined by users of the "blink"
module. These would be defined in the "master" module, and call the
appropriate functions in "gpio" or "spi". These are the proxy
functions, and are a little unusual in that they are declared in
"blinker.h", but defined in "master.c".

Another option is to have hooks defined in a header that is included in
by modules such as "blinker", and which define the functions to be
called for turning the lights on and off. Then the connections are
given in that header, not the blinker module. And the blinker module
can use conditional compilation - if no hook functions are defined, they
are not used.

In both cases, you can use the "blinker", "gpio" and "spi" modules in
test harnesses or real code without changing the source code used.

>
>
>> Think of this as analogous to electronics.  Function pointers are like
>> free connectors on a board that let you re-wire the board when you use
>> it.  That can be very flexible, but makes it very difficult to be sure
>> the board is working and can quickly give you spaghetti systems.  C++
>> virtual functions are like connectors with unique shapes - you can make
>> a few choices of your connections, but only to those points you have
>> specifically allowed.  A "proxy" module is like a multiplexer or buffer
>> for controlling the signal routing, and a header with conditional
>> compilation is like DIP switches or jumpers.
>>
>> Remember, the strength of a programming technique is often best measured
>> in terms of what it /restricts/ you from doing, not from what it
>> /allows/ you to do.  It is more important that it is hard to get things
>> wrong, than to make it easy to get things right.
>>
>>>
>>> Another nice thing that is possible with function pointers is to make
>>> some OOP tricks, for example polymorfism.
>>
>> Don't go there.  Go to C++, rather than a half-baked home-made solution
>> in C.  Using C for OOP like this made sense in the old days - but not
>> now.
>>
>>>
>>> Of course one drawback of using function pointers is stack usage
>>> calculation (see my previous post): it would be impossible to make a
>>> calc, because the tool can't resolve the function pointer call.
>>
>> That is one drawback.  In general, function pointers means you can't
>> make a call-tree from your program.  (Embedded systems generally have a
>> "call forest", since interrupts start their own trees, as do threads or
>> tasks.)  You can't follow the logic of the program, either with tools or
>> manually.
>
> However function pointers usually could assume only a few and fixed
> values, mostly only *one* value in a build (for example, bsp_led_on()
> and bsp_led_off() for my previous example).

Yes, that's true - but there is no way to express that in C. Even if
you use a few specific types (struct typedefs) as parameters to ensure
that only functions with very specific signatures are accepted by the
compiler, it still means /any/ function with a compatible signature
could be used, and your external tools are as helpless for following the
code flow.

> I think that if it's possible to inform the call-graph or stack usage
> tool of these "connections", it would be possible to generate a good
> call graph and worst case stack usage.
>

You can't do that in C.

>
> Another situation where I use function pointers is when I have a
> callback. Just to describe an example, you have two modules: the lower
> level module [battery] that monitors continuously the battery level and
> emit an event (call a function) when the level goes under a custom
> threshold.
> Suppose this event must be acquired by [control] module. One solution
> without function pointers could be:
>
>   /* battery.h */
>   int battery_set_low_thres(uint16_t lvl_mV);
>
>   /* battery.c */
>   #include "battery.h"
>   #include "control.h"
>   ....
>   if (current_lvl < low_thres) {
>     control_battery_low_level_event(current_lvl);
>   }
>   ...
>
>   /* control.h */
>   void control_battery_low_level_event(uint16_t lvl_mV);
>
>   /* control.c */
>   #include "control.h"
>   #include "battery.h"
>
>   void control_battery_low_level_event(uint16_t lvl_mV) {
>     bsp_led_on(BSP_LED_BATTERY_LOW);
>     power_down();
>   }
>
> I don't like it, the lower level module [battery] is stricly coupled to
> [control] module, because it needs control_battery_low_level_event()
> function declaration. Indeed "control.h" must be included in battery.c.
>
> Instead I like an approach that uses a function pointer:
>
>   /* battery.h */
>   typedef void (*battery_low_cb)(uint16_t lvl_mV);
>   int battery_set_low_thres(uint16_t lvl_mV);
>
>   /* battery.c */
>   #include "battery.h"
>   ....
>   static battery_low_cb low_cb;
>   ....
>   if (current_lvl < low_thres) {
>     if (low_cb != NULL) low_cb(current_lvl);
>   }
>   ...
>
>   /* control.h */
>   void control_battery_low_level_event(uint16_t lvl_mV);
>
>   /* control.c */
>   #include "control.h"
>   #include "battery.h"
>
>   static void control_battery_low_level_event(uint16_t lvl_mV);
>
>   void control_init(void) {
>     battery_set_low_thres(2700, control_battery_low_level_event);
>   }
>
>   static void control_battery_low_level_event(uint16_t lvl_mV) {
>     bsp_led_on(BSP_LED_BATTERY_LOW);
>     power_down();
>   }
>
> Here the lower level module [battery] doesn't know anything about the
> higher level module [control], indeed "control.h" isn't included at all
> in battery.c.
>
> It appears to me this approach is perfectly legal and much simpler to
> test because of less linking between modules.
> Because [battery] doesn't depend on other higher level modules, I can
> reuse it in other projects as is.

It is perfectly legal, and may be easier to test - but it is not easier
to analyse, and it is harder to follow the code flow. Don't
misunderstand me - your use of function pointers here is common and
idiomatic. But function pointers have a cost, and alternative
structures can be better (though they too have their costs).

>
> Moreover there are many C projects, even for embedded, that makes large
> use of this approach. For example, many functions in lwip project accept
> function pointers to callback (for example, tcp_connect()[1]).
>
>
> Another approach could be using weak functions:
>
>   /* battery.h */
>   int battery_set_low_thres(uint16_t lvl_mV);
>
>   /* battery.c */
>   #include "battery.h"
>   ....
>   void battery_event_low(uint16_t lvl_mV) __attrinute((weak));
>   void battery_event_low(uint16_t lvl_mV) {
>     UNUSED(lvl_mV);
>   }
>   ....
>   if (current_lvl < low_thres) {
>     if (low_cb != NULL) battery_event_low(current_lvl);
>   }
>   ...
>
>   /* control.h */
>   void control_battery_low_level_event(uint16_t lvl_mV);
>
>   /* control.c */
>   #include "control.h"
>   #include "battery.h"
>
>   void control_init(void) {
>     battery_set_low_thres(2700, control_battery_low_level_event);
>   }
>
>   void battery_event_low(uint16_t lvl_mV) {
>     bsp_led_on(BSP_LED_BATTERY_LOW);
>     power_down();
>   }
>
> [1] https://www.nongnu.org/lwip/2_1_x/group__tcp__raw.html


Click here to read the complete article
Re: Function pointers: good or bad things?

<sn9bia$r6$2@dont-email.me>

 copy mid

https://www.novabbs.com/devel/article-flat.php?id=731&group=comp.arch.embedded#731

 copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: Function pointers: good or bad things?
Date: Fri, 19 Nov 2021 16:21:37 -0700
Organization: A noiseless patient Spider
Lines: 85
Message-ID: <sn9bia$r6$2@dont-email.me>
References: <sn512d$lbo$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Fri, 19 Nov 2021 23:21:47 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="f1e457d5218ac1e41f1783cf17b5280b";
logging-data="870"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+HiRuWlDbFG/1juvgENZyL"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:iRTyD3iSpxvs5AhPjnmM56Re2Sc=
In-Reply-To: <sn512d$lbo$1@dont-email.me>
Content-Language: en-US
X-Mozilla-News-Host: news://nntp.aioe.org
 by: Don Y - Fri, 19 Nov 2021 23:21 UTC

On 11/18/2021 12:58 AM, pozz wrote:
> Are function pointers really bad things?

That's sort of like asking if floats or NULLs are "bad things".
It all boils down to how they are used -- or misused.

Using a pointer to a function when the function can be used
directly, otherwise, is seldom "advisable". Consider:

sort(args, direction_t dir) {
switch dir {
case FORWARD:
fwd_sort(args); break;
case BACKWARDS:
bwd_sort(args); break;
case SIDEWAYS:
sdw_sort(args); break;
case UPSIDEDOWN:
updn_sort(args); break;
}
...
do_other_stuff();
}

contrasted with:

sort(args, direction_t dir) {
(*dir)(args)
...
do_other_stuff();
}

The first implementation avoids use of function pointers by
using a selector ("dir") to determine which particular "_sort()"
to invoke.

The second requires the caller to provide an explicit pointer
to a function that performs that action.

The latter is more flexible in that some unforeseen algorithm
can be used, down the road (e.g., diag_sort()). The first would
have to be recompiled to include an explicit reference to that
"new" algorithm (and a new "selector" -- "DIAGONAL" -- created).

/cf./ qsort(3C)

Function pointers facilitate late binding. Most applications
*know* what their bindings will be at compile time so the
support is often not needed (even if the actual binding is deferred
to runtime)

Along with LATE binding, they facilitate *dynamic* binding -- where
you want to alter some set of invocations programmatically, at
run-time. E.g., call-backs, dispatchers and ISRs.

/cf./ atexit(3C) and similar

I use them heavily in my FSM implementations. It lets me encode
the transition tables into highly compressed forms while still
retaining a high degree of performance and flexibility.

The typical "anxiety" associated with them comes from the syntax
required to properly declare them -- coupled with the whole
notion of "pointers are bad". typedefs are your friend, here.

> Sincerely I found them useful and lastly I was using them more and more. Am I
> wrong?

Use them when needed, but not casually. You don't use long longs for
iterators, do you? Or doubles?

> Of course one drawback of using function pointers is stack usage calculation
> (see my previous post): it would be impossible to make a calc, because the tool
> can't resolve the function pointer call.

Not "impossible" -- just not within the range of capabilities of most
current tools. Clearly *you* could chase down every reference to such
a pointer and examine the value(s) that are used in its place... and,
then, compute the stack usage of each of those functions and add it to the
current stack penetration at the time the function is actually
invoked. So, the information *exists*[1]...

[1] unless the application prompts the user to enter a hexadecimal
constant that it then uses as the address of the function to be
invoked (I've built debugging tools that supported such interfaces).

Re: Function pointers: good or bad things?

<snap0j$n1k$1@dont-email.me>

 copy mid

https://www.novabbs.com/devel/article-flat.php?id=735&group=comp.arch.embedded#735

 copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: david.br...@hesbynett.no (David Brown)
Newsgroups: comp.arch.embedded
Subject: Re: Function pointers: good or bad things?
Date: Sat, 20 Nov 2021 13:17:22 +0100
Organization: A noiseless patient Spider
Lines: 163
Message-ID: <snap0j$n1k$1@dont-email.me>
References: <sn512d$lbo$1@dont-email.me> <sn9bia$r6$2@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Injection-Date: Sat, 20 Nov 2021 12:17:23 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="97de0f5820112279033bb40994fc41ca";
logging-data="23604"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+dmGiIXVav0EX6A53MfCBkR8K6cRB7fBQ="
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101
Thunderbird/78.11.0
Cancel-Lock: sha1:et8ygM7IBL7MggZdFpVM+NWSZhQ=
In-Reply-To: <sn9bia$r6$2@dont-email.me>
Content-Language: en-GB
 by: David Brown - Sat, 20 Nov 2021 12:17 UTC

On 20/11/2021 00:21, Don Y wrote:
> On 11/18/2021 12:58 AM, pozz wrote:
>> Are function pointers really bad things?
>
> That's sort of like asking if floats or NULLs are "bad things".
> It all boils down to how they are used -- or misused.
>
> Using a pointer to a function when the function can be used
> directly, otherwise, is seldom "advisable".  Consider:
>
> sort(args, direction_t dir) {
>   switch dir {
>   case FORWARD:
>      fwd_sort(args); break;
>   case BACKWARDS:
>      bwd_sort(args); break;
>   case SIDEWAYS:
>      sdw_sort(args); break;
>   case UPSIDEDOWN:
>      updn_sort(args); break;
>   }
>   ...
>   do_other_stuff();
> }
>
> contrasted with:
>
> sort(args, direction_t dir) {
>   (*dir)(args)
>   ...
>   do_other_stuff();
> }
>
> The first implementation avoids use of function pointers by
> using a selector ("dir") to determine which particular "_sort()"
> to invoke.
>
> The second requires the caller to provide an explicit pointer
> to a function that performs that action.
>
> The latter is more flexible in that some unforeseen algorithm
> can be used, down the road (e.g., diag_sort()).  The first would
> have to be recompiled to include an explicit reference to that
> "new" algorithm (and a new "selector" -- "DIAGONAL" -- created).
>
> /cf./ qsort(3C)
>
> Function pointers facilitate late binding.  Most applications
> *know* what their bindings will be at compile time so the
> support is often not needed (even if the actual binding is deferred
> to runtime)
>
> Along with LATE binding, they facilitate *dynamic* binding -- where
> you want to alter some set of invocations programmatically, at
> run-time.  E.g., call-backs, dispatchers and ISRs.
>
> /cf./ atexit(3C) and similar

All you write above is true (and it's a useful point to make about
compile-time and run-time binding).

In small embedded systems, however, you almost never /need/ dynamic
run-time binding. Your tasks are known at compile-time - as are your
interrupt functions, your state machines, and lots of other things.
Being able to change things at run-time gives you /no/ benefits in
itself, but significant costs in analysability, code safety, and static
checking (plus possibly significant code efficiency costs).

No one cares if a source module has to be re-compiled - but we /do/ care
if it has to be changed, and has to go through reviews or testing again.
Some kind of late-binding mechanism can help avoid that at times.

However, when we compare the two "sort" versions above, remember that
there are other possible arrangements and other possible pros and cons.
First, note that in the explicit switch, the compiler has more
information - it can, for example, check that all cases are covered
(assuming you have a decent compiler or an external static analysis tool
or linter).

Also note the pattern here. If you want more compact source code that
does not need to be changed when adding diagonal sorting, X-macros are a
good choice. (Or, better, C++ templates.)

>
> I use them heavily in my FSM implementations.  It lets me encode
> the transition tables into highly compressed forms while still
> retaining a high degree of performance and flexibility.
>

Function pointers are certainly often used in such circumstances. I
generally don't use them - because I rarely see compressed forms as
beneficial, they can't be analysed or graphed, you can't follow the code
directly, and you often have /less/ flexibility and significantly less
performance.

However, these things are always a balance, and there are many types of
state machine, many types of code, and many solutions. How readable
code can be depends on who writes it and what other information,
diagrams and documentation is available as much as the code structure.

> The typical "anxiety" associated with them comes from the syntax
> required to properly declare them -- coupled with the whole
> notion of "pointers are bad".  typedefs are your friend, here.
>

I agree that typedefs are your friend here - I'm in less agreement that
the syntax of function pointers is significant to people choosing not to
use them much. But maybe it is relevant for some people.

>> Sincerely I found them useful and lastly I was using them more and
>> more. Am I wrong?
>
> Use them when needed, but not casually.  You don't use long longs for
> iterators, do you?  Or doubles?
>

Agreed - be aware of the pros and cons, and choose carefully according
to your own needs and your own ways of coding.

>> Of course one drawback of using function pointers is stack usage
>> calculation (see my previous post): it would be impossible to make a
>> calc, because the tool can't resolve the function pointer call.
>
> Not "impossible" -- just not within the range of capabilities of most
> current tools.  Clearly *you* could chase down every reference to such
> a pointer and examine the value(s) that are used in its place... and,
> then, compute the stack usage of each of those functions and add it to the
> current stack penetration at the time the function is actually
> invoked.  So, the information *exists*[1]...
>
> [1] unless the application prompts the user to enter a hexadecimal
> constant that it then uses as the address of the function to be
> invoked (I've built debugging tools that supported such interfaces).

Although this thread has been talking about C, I think a comparison with
C++ is worth noting. For much of C++'s history, a major and
heavily-used feature has been class inheritance hierarchies with virtual
functions. These allow late binding - you have "Animal * p; p->run();"
where the actual "run" function depends on the actual type of the
animal. Such virtual functions are a big step up from function pointers
that you have in C, because they are more limited - "run()" is not just
any function, but it is a method for a type that derives from "Animal".
You can't do as much analysis or checking as for compile-time binding,
but it is still vastly better than you can get from C function pointers.

However, modern C++ is moving significantly away from that towards
compile-time polymorphism - templates and generic programming. The
tools in modern C++ have improved greatly for compile-time work, and do
so with every new C++ standard revision. Instead of having a table of
states and actions that is dynamically interpreted at run-time via
function pointers, or manually writing long switch statements for the
job, you can pass the table to a template function and have the optimal
compile-time binding code generated for you.

Instead of the "leds" module (in the first post) having an "led_init"
function that takes two function pointers and having the "led_on"
function calling one of those functions, you now have an "Led<>"
template class that takes a "DigitalOutput" type as a parameter. The
Led<> class is fully flexibly and independent of the actual type of
digital output in use, but the resulting code is as optimal as possible
and it is all generated, checked and analysed at compile-time.

Re: Function pointers: good or bad things?

<snbvln$i4c$1@dont-email.me>

 copy mid

https://www.novabbs.com/devel/article-flat.php?id=742&group=comp.arch.embedded#742

 copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: Function pointers: good or bad things?
Date: Sat, 20 Nov 2021 16:16:57 -0700
Organization: A noiseless patient Spider
Lines: 120
Message-ID: <snbvln$i4c$1@dont-email.me>
References: <sn512d$lbo$1@dont-email.me> <sn9bia$r6$2@dont-email.me>
<snap0j$n1k$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Sat, 20 Nov 2021 23:17:11 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="09c0c2c06372fbd9c5f1a59300985b81";
logging-data="18572"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+o+cDztDwNWEJR4VBiAxaE"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:agFPUwZBlXln+Gaevz+vn32EHTY=
In-Reply-To: <snap0j$n1k$1@dont-email.me>
Content-Language: en-US
 by: Don Y - Sat, 20 Nov 2021 23:16 UTC

On 11/20/2021 5:17 AM, David Brown wrote:
> In small embedded systems, however, you almost never /need/ dynamic
> run-time binding. Your tasks are known at compile-time - as are your
> interrupt functions, your state machines, and lots of other things.
> Being able to change things at run-time gives you /no/ benefits in
> itself, but significant costs in analysability, code safety, and static
> checking (plus possibly significant code efficiency costs).

Define "small". In the late 70's, early 80's, I was working with
"small" 8b devices in 64KB address spaces. ROM and RAM were precious
so often in short supply.

Yet, we found it advantageous to implement run-time linking in our
products. The processor would do some essential testing, on power up.
Then, probe the memory space for "ROMs" (typically located on
other cards). Finding a ROM, it would examine a descriptor that
declared entry points ("functions") *in* that ROM before moving on
to locate the next ROM.

It would then invoke the "POST" entry point for each ROM that declared
one to finish POST.

Assuming that went as planned, it would invoke the "init()" entry
point. The ROM's init() could query the processor and other ROMs
for routines that it needed. In each case, a "fixup" table was built
and revised to allow CALLs (everything was ASM, back then) to be
dispatched through the RAM-based fixup tables to piece the code
together.

So, "display_character()" on a 7-segment display board could be
implemented entirely differently than "display_character()" on
a graphic LCD.

The 7-segment display board might be interested in the "line_frequency()"
determined by the power supply board (to minimize beat against ambient
light sources) -- while the LCD display might have no interest.

This allowed us to swap out boards without having to rebuild the
sources for any of the other boards.

It also allowed us to design "plug-in modules" (the size of a
pack of cigarettes) that could contain code libraries that
were accessed by the user (so, the number and names of the
functions within were unknown to the rest of the system
UNTIL that module was plugged in).

> No one cares if a source module has to be re-compiled - but we /do/ care
> if it has to be changed, and has to go through reviews or testing again.
> Some kind of late-binding mechanism can help avoid that at times.

If you ware developing for a regulated industry (medical, pharma,
aviation, gaming, etc.) you *do* care about having to recompile
because you now have a different codebase -- that must be validated.

OTOH, if you've validated your "display board" against its
interface contract, you can use that with any compatible system
that *expects* that interface. New display board? Validate *its*
code -- and only its code.

>> I use them heavily in my FSM implementations. It lets me encode
>> the transition tables into highly compressed forms while still
>> retaining a high degree of performance and flexibility.
>
> Function pointers are certainly often used in such circumstances. I
> generally don't use them - because I rarely see compressed forms as
> beneficial, they can't be analysed or graphed, you can't follow the code
> directly, and you often have /less/ flexibility and significantly less
> performance.

If you have a few hundred states in an FSM and each state has a dozen
or more branches out to other states, the space consumed by the
state tables quickly escalates.

test_criteria_a() &tested_item &next_state_table transition_routine()
..
test_criteria_c() &other_item &other_state_table other_transition()
..
..
..
test_criteria_a() &other_item &another_state_table one_more_transition()

The biggest modules in my applications are usually those that
implement the "logic" of the UI -- because there are often so many
interactions and competing issues that have to be addressed
(contrast that with the code required for a UART driver)

Additionally, this reduces the effort to implement the FSM thereby
ensuring *each* potential condition and transition is processed
identically. No risk of the developer forgetting to do <whatever>
on some particular transition.

>>> Sincerely I found them useful and lastly I was using them more and
>>> more. Am I wrong?
>>
>> Use them when needed, but not casually. You don't use long longs for
>> iterators, do you? Or doubles?
>
> Agreed - be aware of the pros and cons, and choose carefully according
> to your own needs and your own ways of coding.

No. You always have to consider that someone else WILL end up having to
"deal with" your code. You want your code to be understandable without
requiring a tedious, detailed examination. Folks maintaining code tend
not to have the time to spend "trying on" the code to see how it fits.
They have to be able to quickly AND ACCURATELY understand what you are doing.
If they have trouble with expression syntax and might misunderstand
what you are doing, then "your way" was likely not the RIGHT way.

Be able to justify why you make design and implementation decisions.
Not just because it's "fun" or "elegant".

I suspect we've all encountered products that were overly complex (to
use) -- because the developer thought he was "giving the user flexibility"
(more flexible is better, right?).

Function pointers afford lots of flexibility. But, they also leave you
always wondering if there is some *other* value that may be used and
if your understanding of the code would change -- had you known of it's
existence. (the first sort() nails down ALL the possibilities; the
second one leaves you forever uncertain...)

Re: Function pointers: good or bad things?

<sndg7g$m73$1@dont-email.me>

 copy mid

https://www.novabbs.com/devel/article-flat.php?id=743&group=comp.arch.embedded#743

 copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: david.br...@hesbynett.no (David Brown)
Newsgroups: comp.arch.embedded
Subject: Re: Function pointers: good or bad things?
Date: Sun, 21 Nov 2021 14:05:51 +0100
Organization: A noiseless patient Spider
Lines: 60
Message-ID: <sndg7g$m73$1@dont-email.me>
References: <sn512d$lbo$1@dont-email.me> <sn9bia$r6$2@dont-email.me>
<snap0j$n1k$1@dont-email.me> <snbvln$i4c$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit
Injection-Date: Sun, 21 Nov 2021 13:05:52 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="fd2cea7d8890ce219ea71576e2c3ed94";
logging-data="22755"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19kd9MZqMA65NDfH5hq/Vf/r0h+3JCrkow="
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101
Thunderbird/78.11.0
Cancel-Lock: sha1:waLk2AOuLWekUwaW8qUuK/mCfUM=
In-Reply-To: <snbvln$i4c$1@dont-email.me>
Content-Language: en-GB
 by: David Brown - Sun, 21 Nov 2021 13:05 UTC

On 21/11/2021 00:16, Don Y wrote:
> On 11/20/2021 5:17 AM, David Brown wrote:
>> In small embedded systems, however, you almost never /need/ dynamic
>> run-time binding.  Your tasks are known at compile-time - as are your
>> interrupt functions, your state machines, and lots of other things.
>> Being able to change things at run-time gives you /no/ benefits in
>> itself, but significant costs in analysability, code safety, and static
>> checking (plus possibly significant code efficiency costs).
>
> Define "small".  In the late 70's, early 80's, I was working with
> "small" 8b devices in 64KB address spaces.  ROM and RAM were precious
> so often in short supply.
>

Small-systems embedded programming is about dedicated devices with
dedicated programs, rather than the size of the device.

<snip war stories>

>> No one cares if a source module has to be re-compiled - but we /do/ care
>> if it has to be changed, and has to go through reviews or testing again.
>>   Some kind of late-binding mechanism can help avoid that at times.
>
> If you ware developing for a regulated industry (medical, pharma,
> aviation, gaming, etc.) you *do* care about having to recompile
> because you now have a different codebase -- that must be validated.
>

You care more about the source than the compiled code, but yes, you /do/
care about having to recompile. However, there is very little
difference between recompiling one module in a program or many - the
resulting binary changes and must be tested and qualified appropriately.

> OTOH, if you've validated your "display board" against its
> interface contract, you can use that with any compatible system
> that *expects* that interface.  New display board?  Validate *its*
> code -- and only its code.

We are not talking about separate devices here. The posts are long
enough without extra side-tracking.

>
>>> I use them heavily in my FSM implementations.  It lets me encode
>>> the transition tables into highly compressed forms while still
>>> retaining a high degree of performance and flexibility.
>>
>> Function pointers are certainly often used in such circumstances.  I
>> generally don't use them - because I rarely see compressed forms as
>> beneficial, they can't be analysed or graphed, you can't follow the code
>> directly, and you often have /less/ flexibility and significantly less
>> performance.
>
> If you have a few hundred states in an FSM and each state has a dozen
> or more branches out to other states, the space consumed by the
> state tables quickly escalates.

Your design is totally and utterly broken, so there is no point in
pretending there is a "good" way to implement it. Throw it out and
start again, by dividing the problem into manageable pieces.

Re: Function pointers: good or bad things?

<soat35$a20$1@dont-email.me>

 copy mid

https://www.novabbs.com/devel/article-flat.php?id=747&group=comp.arch.embedded#747

 copy link   Newsgroups: comp.arch.embedded
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: blockedo...@foo.invalid (Don Y)
Newsgroups: comp.arch.embedded
Subject: Re: Function pointers: good or bad things?
Date: Thu, 2 Dec 2021 09:43:06 -0700
Organization: A noiseless patient Spider
Lines: 307
Message-ID: <soat35$a20$1@dont-email.me>
References: <sn512d$lbo$1@dont-email.me> <sn9bia$r6$2@dont-email.me>
<snap0j$n1k$1@dont-email.me> <snbvln$i4c$1@dont-email.me>
<sndg7g$m73$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Thu, 2 Dec 2021 16:43:18 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="97338051e9042b5541a3e6f2ec31e7f9";
logging-data="10304"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+WA3AFpB+UEUHhE4MOgrOJ"
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101
Thunderbird/52.1.1
Cancel-Lock: sha1:byjvWDZP32d0AIQO15VmFtLYkjY=
In-Reply-To: <sndg7g$m73$1@dont-email.me>
Content-Language: en-US
X-Mozilla-News-Host: news://nntp.aioe.org
 by: Don Y - Thu, 2 Dec 2021 16:43 UTC

[Sorry for the delay, I tend not to watch c.a.e anymore (lack of
"interesting" traffic]

On 11/21/2021 6:05 AM, David Brown wrote:
> On 21/11/2021 00:16, Don Y wrote:
>> On 11/20/2021 5:17 AM, David Brown wrote:
>>> In small embedded systems, however, you almost never /need/ dynamic
>>> run-time binding. Your tasks are known at compile-time - as are your
>>> interrupt functions, your state machines, and lots of other things.
>>> Being able to change things at run-time gives you /no/ benefits in
>>> itself, but significant costs in analysability, code safety, and static
>>> checking (plus possibly significant code efficiency costs).
>>
>> Define "small". In the late 70's, early 80's, I was working with
>> "small" 8b devices in 64KB address spaces. ROM and RAM were precious
>> so often in short supply.
>
> Small-systems embedded programming is about dedicated devices with
> dedicated programs, rather than the size of the device.

So, static link a Linux kernel, slap it in a "closed"/dedicated
functionality device (like a DVR?) and it's now "small"?

I consider "small" to be an indication of relative complexity.

Less resources (small) *tends* to be less complex. A "mouse"
is a small system. An MRI scanner isn't. Both can be implemented
however their designers CHOSE to implement them -- none is likely
going to morph into a chess program with the flip of a configuration
switch (so, no *need* to be able to dynamically RElink)!

> <snip war stories>

No, REAL examples of how this was used to advantage. Instead of
baseless claims with nothing to back them up.

>>> No one cares if a source module has to be re-compiled - but we /do/ care
>>> if it has to be changed, and has to go through reviews or testing again.
>>> Some kind of late-binding mechanism can help avoid that at times.
>>
>> If you ware developing for a regulated industry (medical, pharma,
>> aviation, gaming, etc.) you *do* care about having to recompile
>> because you now have a different codebase -- that must be validated.
>
> You care more about the source than the compiled code, but yes, you /do/
> care about having to recompile. However, there is very little
> difference between recompiling one module in a program or many - the
> resulting binary changes and must be tested and qualified appropriately.

There's a huge difference! You only have to test the *component*
that has been modified -- not the entire system!

If you use COTS devices (hardware/software), do you *validate* their
designs? (against what -- a "marketing blurb"?) Are you sure all
the setup and hold times for signals are met (where did you find the
schematics?)? All the calling parameters and return values of
all the internal routines used? (source code availability?)

You likely just "trust" that they've done things "right" and
hope for the best. (after all, what *can* you do to convince
yourself of that?)

When *you* are the creator of those "components", you have access
to all of this information and can *ensure* that your vendor
(i.e., yourself) has produced the product that they claim to
have produced.

I can throw together a product that requires very little final
assembly testing if I've already tested/validated all of the components.
Treating the software that drives a component as part of the component,
FROZEN in silicon (ROM) reduces the size and complexity of the
portions that are yet-to-be-tested (e.g., the application layer)

Did they revalidate the *entire* 737MAX design?? (if you think
they did, then gotta wonder why it took so long to validate it
the *first* time 'round!)

>> OTOH, if you've validated your "display board" against its
>> interface contract, you can use that with any compatible system
>> that *expects* that interface. New display board? Validate *its*
>> code -- and only its code.
>
> We are not talking about separate devices here. The posts are long
> enough without extra side-tracking.

You've missed the point, entirely. Read more carefully:

"Then, probe the memory space for 'ROMs' (typically located on
other cards). Finding a ROM, it would examine a descriptor that
declared entry points ("functions") *in* that ROM before moving on
to locate the next ROM."

There's only one "device". It is implemented using multiple boards
("cards") -- as a good many "devices" are NOT implemented on "single
PCBs". For example:

- One board has the processor and whatever devices seem appropriate.

- Another board has a display (imagine a bunch of 7-segment LEDs
and drive electronics... or PGDs, VFDs, LCDs, etc.) along with
a ROM containing code that knows how to "talk" to the hardware
on *that* board -- BUT NO PROCESSOR.

- Another has the power supply and "power interface" (to monitor
voltage, battery, charger, line frequency, mains power available,
etc.) along with a ROM containing code to talk to the hardware
on *that* board -- BUT, again, NO PROCESSOR.

A "bus" connects them -- so *the* processor on the main board can
interact with the hardware on those cards. And, coincidentally, it
can access the ROM(s) containing the code (that DOES that interaction)!

I.e., the code for the *single* device is scattered across
three boards (in this case). The product glues them together
at run time. Power down the product. Swap out a board with
another that is *functionally* equivalent but potentially
implemented entirely differently (e.g., a different power
supply for a different market; or a different display technology
for use in a different environment) and the product FUNCTIONS
the same as it did before power was removed.

There are *huge* advantages to this approach -- esp in prototyping
new systems. How long do you want to wait to have functional
hardware on which to run your application? If you're buying COTS
modules, you're essentially doing this -- except module X from
vendor A likely won't have *code* on it that will seemlessly interface
with *code* on module Y from vendor B!

They *may* provide you with some "sample code" to assist with your
development. And, I'm *sure* that was written with YOUR needs in
mind? (not!)

I use the same approach in my current project -- except the boards
are *tiny* (~3 sq in) and the ROMs are virtual -- downloaded over
a network interface. So, the *internal* ROM in the processor can be
identical (because "processor boards" are *all* identical!) and, yet,
support any number of add-on boards, with the appropriate software
loaded and configured at run-time (the processor just has to identify
each connected board/module and request associated "firmware" -- in
a manner similar to a kernel "probing" the hardware available in
the environment in which it finds itself executing. Easy to do with
tiny MCUs, nowadays, which can also add "addressable functionality"
(like "ensure the card is powered down on reset -- until the processor
directs you to power it up")

Want to drive a speaker AND a video display? Put a speaker board
and a video display board on a processor, power up, wait for code
to load and you're all set. Want to scatter those functions onto
different "nodes"? Put the speaker board on one processor and the
video board on another. No change to software.

>>>> I use them heavily in my FSM implementations. It lets me encode
>>>> the transition tables into highly compressed forms while still
>>>> retaining a high degree of performance and flexibility.
>>>
>>> Function pointers are certainly often used in such circumstances. I
>>> generally don't use them - because I rarely see compressed forms as
>>> beneficial, they can't be analysed or graphed, you can't follow the code
>>> directly, and you often have /less/ flexibility and significantly less
>>> performance.
>>
>> If you have a few hundred states in an FSM and each state has a dozen
>> or more branches out to other states, the space consumed by the
>> state tables quickly escalates.
>
> Your design is totally and utterly broken, so there is no point in
> pretending there is a "good" way to implement it. Throw it out and
> start again, by dividing the problem into manageable pieces.

Amusing conclusion.

You're suggesting an *application* that is inherently of sufficient
complexity to require hundreds of states to REPRESENT, should NOT be
implemented by EXPOSING those states, the stimuli to which each responds
and the follow-on states? Instead, it should be implemented in some
other, likely LESS OBVIOUS way -- that makes it *easier* to determine
when a particular stimulus is being "unhandled"?


Click here to read the complete article
1
server_pubkey.txt

rocksolid light 0.9.7
clearnet tor