-1

How to do that?

The code is causing: null pointer dereference.

10-08 17:26:00.835  5249  5617 D DigestGenerator:  /apex/com.android.runtime/lib/bionic/libc.so!libc.so (strstr+) ()

10-08 17:26:00.835  5249  5617 D DigestGenerator:  XXbase.apk!libtest.so (test_bool(char const*)+) ()
bool test_bool(const char *filename) {
    if (strstr(filename, "yes")) {
        return true;
    }
    return false;
}

long *test_long(long number, ...) {
    va_list args;
    va_start(args, number);
    for(int i=0; i<number; ++i) {
        if (test_bool(va_arg(args, const char *))) {
            //do something
            return 0;
        }
    }
    va_end(args);
    //do something
    return 0;
}

test_long(0, 1, "yes", 3);
10
  • Show how you're calling test_long(). Commented Oct 8, 2024 at 21:33
  • 4
    There's no built-in way to know the type of a varargs argument, you need to encode it somehow yourself. For instance, printf() determines the types from the format string. Commented Oct 8, 2024 at 21:45
  • 1
    If that is the case then your loop never runs Commented Oct 8, 2024 at 21:49
  • 1
    test_long(0, 1, "yes", 3) - but 1 and 3 are not a const char *. You can't call va_arg(.., const char*) when the argument is not a const char*. Commented Oct 8, 2024 at 21:51
  • 4
    Use a variadic template instead, and then your type information won't be lost. i.e. template <typename... Args> long test_long(Args... args) { ... } Commented Oct 8, 2024 at 22:25

1 Answer 1

1

C-style variadic arguments don't carry any type information with them. Your function has to know what args are there before trying to read them. For instance, printf uses the format string to determine what types of arguments are present. You could do something similar, i.e.

long test_long(std::string_view types, ...)
{
    va_list args;
    va_start(args, types);
    for (char c : types) {
        if (c == 'l') {
            long l = va_arg(args, long);
            // do something with l
        } else if (c == 's') {
            const char* s = va_arg(args, const char*);
            // do something with s
        } else {
            // unknown type
            return 0;
        }
    }
    return 1;
}

int main()
{
    test_long("llsl", 0, 1, "yes", 3);
}

Demo

This is fairly brittle though, and you have to explicitly handle every possible type.


A more C++-style approach would be to use a variadic template instead of C-style va_args. For instance:

template <typename... Args>
long test_long(Args... args)
{
    bool results[] = { test_bool(args)... };
    // do something with the results
    return 1;
}

int main()
{
    test_long(0, 1, "yes", 3);
}

Demo

This may be a bit more complex simply due to C++ templates being complex, but its far more type safe than the C-style approach since the type of arguments isn't lost.

In embedded context this may have the downside of increasing code size though, since each different set of parameter types results in a separate function being instantiated from the template.

Sign up to request clarification or add additional context in comments.

5 Comments

Your template example does not compile, as you still can't pass 1 and 3 to test_bool() (your demo is not actually demonstrating what this code is showing). You can simply overload test_bool() for non-string inputs (demo). Or you can use if constexpr to test if a given argument is (compatible with) a char* before treating it as such (demo).
@RemyLebeau Yes, I was assuming the "real" test_bool was a family of functions that's overloaded for different types.
To be accurate, templates do not increase code size, they may increase the executable size depending on how many templates are instantiated, but the code itself won't change by itself :)
@Fareanor Machine code is still code. Most MCUs refer to "code ROM" or similar, which is what's getting eaten by increased (executable) code size.
Your template can type-erase the data and forward them to a single implementation. The house keeping chore of type-erasure needs to be handled, either manually or automatically; the cost of code size inevitable in either case. If type-erasure is not possible, again the cost of code-size is inevitable; somebody must produce the code for each special case; either a clean template, or a mess of multiple functions/overloads.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.