Chained functions in C

Started by
5 comments, last by ferrous 10 years, 1 month ago

(This is C btw, not C++)

I just came across the following. Regulator_t is a struct holding various parameters.


/* only one of these */
Regulator_t theRegulator;

int main()
{

    /* initialise regulator */
    set_coil_value( 0.0001f,                   /* 100uH coil */
    set_maximum_coil_current( 1.5f,            /* allow 1.5 amps */
    set_maximum_output_voltage( 200.0f,        /* cap output voltage to 200 volts */
    set_switching_voltage( 325.0f,         /* voltage from rectifier is 230*sqrt(2) = 325V */
    set_output_voltage_divider_ratio( 100.0f,  /* 1Meg to 10k is a ratio of 100 */
    initialise_regulator( theRegulator ))))));

    /* ---SNIP--- */
}

I'm not sure how to feel about this. Someone was obviously trying to be a smartass by defining all "set" functions with the pattern:


Regulator_t* set_whatever( float someValue, Regulator_t* regulator )
{
    regulator->someValue = someValue;
    /* do other important stuff */
    return regulator;
}

On one hand, it's a little confusing because the first section of code is executed "backwards", i.e. initialise_regulator is called first and then the chain is executed from the inside out.

On the other hand, it might actually be easier to read than doing it the traditional way:


initialise_regulator( theRegulator );
set_coil_value( theRegulator, 0.0001f );
set_maximum_coil_current( theRegulator, 1.5f );
/* etc */

Any thoughts?

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
Advertisement

I think it's nonidiomatic, hard to read, and far too Klever™.

The idiomatic way to chain functions in a C-based language is to return a pointer or reference to the object.


initialise_regulator(&theRegulator)->set_coil_value(0.0001f)->set_maximum_coil_current(1.5f)->...;

That way, the order of operations and the textual ordering are the same. In C proper, the traditional way accomplishes the same thing.

You write programs for human readers. If you try to make it harder for them (and "them" is more likely "you" a few months later), you're just being an ass.

Stephen M. Webb
Professional Free Software Developer

Yes, I guess it makes sense from a theoretical perspective as composition of functions, but I find it quite unreadable. I imagine he was inspired by method chaining in e.g. Java where methods mutate their instance and then return a reference to it, so you can write object.DoThis().ThenThat().ThisToo(), but due to the syntactic sugar it's actually executed left to right in that case. You could do the same thing in C using function pointers in the struct but as someone mentioned in your other thread this is hard to maintain, and you still need to pass "this" explicitly (excluding dirty hacks).

Would not recommend.

The idiomatic way to chain functions in a C-based language is to return a pointer to the object.


initialise_regulator(&theRegulator)->set_coil_value(0.0001f)->set_maximum_coil_current(1.5f)->...;

That way, the order of operations and the textual ordering are the same.

I'm pretty sure the function needs a pointer to the struct instance to be able to act on the data, so it gets tedious real fast if the variable has a longish name (though you can add newlines to tidy it up a bit, but then why don't you just use a proper initializer instead of abusing getters and setters for initialization purposes).

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

I'm more interested in


    set_coil_value( 0.0001f,                   /* 100uH coil */
    set_maximum_coil_current( 1.5f,            /* allow 1.5 amps */
    set_maximum_output_voltage( 200.0f,        /* cap output voltage to 200 volts */
    set_switching_voltage( 325.0f,         /* voltage from rectifier is 230*sqrt(2) = 325V */
    set_output_voltage_divider_ratio( 100.0f,  /* 1Meg to 10k is a ratio of 100 */
    initialise_regulator( theRegulator ))))));

Do you know what this code is attached to ?

That looks like the settings for a transformer . *shrugs *

Seems much easier to use my HART communicator to soft set those values, than hard set them VIA code injection.

I cannot remember the books I've read any more than the meals I have eaten; even so, they have made me.

~ Ralph Waldo Emerson

looks like it was written by a Lisp programmer.


I think it's nonidiomatic, hard to read, and far too Klever™


Yes, I guess it makes sense from a theoretical perspective as composition of functions, but I find it quite unreadable. I imagine he was inspired by method chaining in e.g. Java where methods mutate their instance and then return a reference to it, so you can write object.DoThis().ThenThat().ThisToo(), but due to the syntactic sugar it's actually executed left to right in that case. You could do the same thing in C using function pointers in the struct but as someone mentioned in your other thread this is hard to maintain, and you still need to pass "this" explicitly (excluding dirty hacks).



Would not recommend.

My thoughts exactly.




Bregma, on 12 Mar 2014 - 1:52 PM, said:
[quote name='Bregma' timestamp='1394628765' post='5138384']
The idiomatic way to chain functions in a C-based language is to return a pointer or reference to the object.

initialise_regulator(&theRegulator)->set_coil_value(0.0001f)->set_maximum_coil_current(1.5f)->...;

That way, the order of operations and the textual ordering are the same. In C proper, the traditional way accomplishes the same thing.
[/quote]

I'm pretty sure the function needs a pointer to the struct instance to be able to act on the data, so it gets tedious real fast if the variable has a longish name (though you can add newlines to tidy it up a bit, but then why don't you just use a proper initializer instead of abusing getters and setters for initialization purposes).

As Bacterius pointed out, the struct would have to hold a reference to every function to make that work, and you'd additionally have to pass the instantiated variable along with it:


initialise_regulator(&theRegulator)->set_coil_value(&theRegulator, 0.0001f)->set_maximum_coil_current(&theRegulator, 1.5f)->...;

You may as well spray camel's diarrhea in my eyes.

I'm more interested in


    set_coil_value( 0.0001f,                   /* 100uH coil */
    set_maximum_coil_current( 1.5f,            /* allow 1.5 amps */
    set_maximum_output_voltage( 200.0f,        /* cap output voltage to 200 volts */
    set_switching_voltage( 325.0f,         /* voltage from rectifier is 230*sqrt(2) = 325V */
    set_output_voltage_divider_ratio( 100.0f,  /* 1Meg to 10k is a ratio of 100 */
    initialise_regulator( theRegulator ))))));

Do you know what this code is attached to ?

That looks like the settings for a transformer . *shrugs *

Seems much easier to use my HART communicator to soft set those values, than hard set them VIA code injection.

You're pretty much spot on. It's part of a high power buck regulator (step down transformer). The values are hard-coded simply because there is only one version of hardware, and dynamically programming them would introduce additional sources of potential errors, since false values have disastrous effects like the power MOSFETs blowing up.

Much of the control logic was offloaded to a micro controller to save space, components, and price . The micro controller is only 0.79$ and yet is essentially a miniature oscilloscope. It can do what 50$ of hardware could have done, only faster and more accurately. The only downside is now you're more error-prone to software bugs, so I'm heavily relying on unit testing.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty

Is it doing some magic under the hood with those set functions? It seems like a simple function like, "InitDefaults(reg*) would do the same thing and be a bit clearer. Granted it obscures the default values out of it, but if you are truly hardcoding those values anyway, does that matter? Or, to be clearer, a specific function like InitToHardwareXValues()?

Hell, to be honest, unless you have a lot of programmers touching that structure, I'd be tempted to just make those values public.

This topic is closed to new replies.

Advertisement