Callback into C with a return value

I have a C API which takes a callback and examines the integer return value of that callback to decide what to do: a return value of 0 means stop doing stuff, any other value means continue to block and process stuff. I have exposed this callback to mJS but my problem is that the integer return value seen by the C code is always coming out as 1. What am I doing wrong? How can I get the correct integer return value in my C code?

Here’s how I am coding it:

// The callback is of this form
int cb(void *, void *);
int (*pgStoredCb)(void *, void *) = NULL;
void *pgStoredCbParams = NULL
void *pgStoredCbUserData = NULL

// Set up the callback
void setCb(int(*pCb) (void *, void *), void *pCbParams, void *pCbUserData) {
    pgStoredCb = pCb;
    pgStoredCbParams = pCbParams;
    pgStoredCbUserData = pCbUserData;
}

// Use the callback
void eventHasOccurred{} {
    if (pgStoredCb != NULL) {
         if (pgStoredCb(pgStoredCbParams, pgStoredCbUserData)) {
            printf("Callback returned true.\n");
        }
    }
}

…and in mJS it is used like this:

let cb = function(pParam) {
    print("Callback has been called with \"", pParam, "\" and is returning 0.\n");
    return 0;
};

let cbParam = "Hello World!";
let cbSet = ffi('void setCb(int(*) (void *, userdata), void *pCbParams, userdata');
cbSet(cb, cbParam, null);

After correcting some syntax errors in your example code it runs as expected for different values of retVal:
mJS

let cb = function (pParam) {
  let retVal = -2;
  print("init.js - Callback has been called with \"", pParam, "\" and is returning ", retVal);
  return retVal;
};

C

void eventHasOccurred() {
  if (pgStoredCb != NULL) {
    int ret_val = pgStoredCb(pgStoredCbParams, pgStoredCbUserData);
    LOG(LL_INFO,
        ("%s:%d - Callback returned %d.\n", __FUNCTION__, __LINE__, ret_val));
  }
}

Output:

[May 17 15:03:48.557] init.js - Callback has been called with " <foreign_ptr@3ffcbac9> " and is returning  -2
[May 17 15:03:48.563] main.c:19               eventHasOccurred:19 - Callback returned -2.

[May 17 15:04:51.787] init.js - Callback has been called with " <foreign_ptr@3ffcbac5> " and is returning  0
[May 17 15:04:51.794] main.c:19               eventHasOccurred:19 - Callback returned 0.

[May 17 15:05:38.246] init.js - Callback has been called with " <foreign_ptr@3ffcbac5> " and is returning  3
[May 17 15:05:38.253] main.c:19               eventHasOccurred:19 - Callback returned 3.
1 Like

Thanks for trying that: as you have spotted, my code was an illustration of what I’m doing rather than actually what I’m doing, since the case in question is buried inside a larger block of code. I will debug a bit further and reply again here.

Well, it looks like the issue I had is down to types. The actual C API that needs to be called expects a callback which returns a bool and, of course, doesn’t have the userdata parmeter that mJS needs, so I had to insert a “relay” function which returned an int and added the void * parameter for userdata, something like this (and here I have inserted some debug for this problem):

static bool cbRelay(void *pParam)
{
    bool returnValue = false;
    int32_t returnValueInt = 0;

    if (gpStoredCb!= NULL) {
        returnValue = gpStoredCb(pParam, gpStoredCbUserData);
        printf("returnValue %d.\n", returnValue);
        returnValueInt = gpStoredCb(pParam, gpStoredCbUserData);
        printf("returnValueInt %d.\n", returnValueInt);
    }

    return returnValue;
}

The issue lies in the bool part.

If in JS I return 6 then returnValue is 1 and returnValueInt is, indeed, 6, which I guess is fair enough (though I have to say that I didn’t realise the compiler was going to force 6 to 1 for the bool, must be some newfangled C requirement :slight_smile:). However, if in JS I return true or I return false then in both cases returnValue is 1 and returnValueInt is -1. This is why I thought the return value was broken, 'cos it’s the same for true or false (and indeed for 6 if you’re only looking at the bool value and don’t realise compilers have moved on). Of course, if in JS I return 0 or 1 then I get the expected values back in C code, so I will do that, it just seems a bit odd that the bool values don’t work as one might expect; the joys of Javascript best effort type munging I guess :slight_smile: .

let FALSE=0;
let TRUE=1;

:grinning:

1 Like

I’ve tried your code but have:

MJS error: bad ffi signature: “void setCb(int(*) (void *, userdata), void *pCbParams, userdata”: 2

Sorry, yes, as @nliviu pointed out, there were syntax errors in the code I typed above, it was only meant as an illustration of the problem not the code I actually used. The line:

let cbSet = ffi('void setCb(int(*) (void *, userdata), void *pCbParams, userdata');

…has a bracket missing, should be:

let cbSet = ffi('void setCb(int(*) (void *, userdata), void *pCbParams, userdata)');

Other syntax errors may also be present!

txs!

I think the line is:

let cbSet = ffi(‘void setCb(int(*) (void *, userdata), void ( * ), userdata)’);

but, in this case I have:

MJS error: bad ffi signature: “void setCb(int(*) (void *, userdata), void ( * ), userdata)”: only one callback is allowed

In mjs_ffi.c there is this comment:
/*
* We already have parsed some callback argument. Currently we don’t
* support more than one callback argument, so, return error
* TODO(dfrank): probably improve
*/

SOLVED!!!
Thanks to all!

Ah, no, that would give you two callbacks, in this case there is only one callback but it takes two parameters: a void * and a userdata (which, as far as C is considered, will be a void * but it has to be called userdata in the function signature inside mJS so that mJS can make special use of it).

It really is:

let cbSet = ffi('void setCb(int(*) (void *, userdata), void *pCbParams, userdata)');

Actually it is

let cbSet = ffi('void setCb(int(*) (void *, userdata), void *, userdata)');

(No parameter name in ffi. Only types.)

1 Like

This is the complete code that works:
C:

int (*pgStoredCb)(void *, void *) = NULL;
void *pgStoredCbParams = NULL;
void *pgStoredCbUserData = NULL;

// Set up the callback
void setCb(int(*pCb) (void *, void *), void *pCbParams, void *pCbUserData) {
pgStoredCb = pCb;
pgStoredCbParams = pCbParams;
pgStoredCbUserData = pCbUserData;
}

// Use the callback
void eventHasOccurred() {
if (pgStoredCb != NULL) {
int ret_val = pgStoredCb(pgStoredCbParams, pgStoredCbUserData);
ESP_LOGI(TAG, “eventHasOccurred: %s:%d - Callback returned %d.\n”, FUNCTION, LINE, ret_val);
}
}

JS:

let cb = function (pParam) {
let retVal = -2;
print(’–mjs: Callback has been called with ', pParam, ’ and is returning ', retVal);
return retVal;
};
let cbParam = ‘Hello World!’;
let cbSet = ffi(‘void setCb(int(*) (void *, userdata), void *, userdata)’);
cbSet(cb, cbParam, null);

the only error is in JS code that print:

–mjs: Callback has been called with <foreign_ptr@3fffa1c4> and is returning ld

ld, not -2