1

I have a function of the following signature:

void foo(int argc, char const * const argv[]);

I would like to call it in a concise way, similar, but not necessary identical as below:

foo(3, {"one", "two", "three"});

I know that C supports compound literals just for this purpose (reference).

I also know how to solve the problem using templates (reference).

However, my problem is slightly different. The function signature is fixed - it normally takes arguments passed to main and the size of the array is not predefined. I am writing tests for this function and in my case the passed arrays are known at compile-time.

Is there a way to call foo without using temporary variables or wrapper functions like below?

char const * array[3] = {"one", "two", "three"};
foo(3, array);
template<int N>
void wrapper(char const * const (&array)[N])
{
    foo(N, array);
}
16
  • 4
    I'd recommend changing the function to take a std::array or std::vector. That's going to make your life simpler. Commented Jan 19, 2020 at 15:43
  • @JesperJuhl And how do you pass argc and argv to a std::array? You can't. I already have a wrapper that takes std::vector, but I would like to eliminate it if possible. Commented Jan 19, 2020 at 15:51
  • 1
    Isn't this a case for an initializer_list? Commented Jan 19, 2020 at 16:11
  • 1
    @KrzysiekKarbowiak: Why wouldn't you want to use the C++ wrapper you wrote? It's a much better interface as you are not allowed to lie about the size of the array. The size of the array given to the underlying function will always be correct. Commented Jan 19, 2020 at 17:12
  • 1
    Have you tried enable_if<true, char const*[]>::type{"one", "two", "three"}? Why wouldn't it work? Commented Jan 19, 2020 at 17:13

2 Answers 2

2

though I have no idea how compiler works, it works:

template<size_t N>
void foo(int argc, const char *(&&argv)[N]){
    int i=0;
    for( auto o: argv) {
        ++i;
        cout<<"array"<<i<<" = " << o<<"\n";
    }

    cout<< "\narray size "<<i<<"\n"<<argc<<"\n";
    return;
}

int main(){

    foo(3, {"one", "two", "three"});

}

array1 = one
array2 = two
array3 = three

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

Comments

0

You can write a simple wrapper for arguments themselves.

Example (see below):

auto wrapper(std::initializer_list<const char*> lst) {
    return lst.begin();
}

foo(3, wrapper({"one", "two", "three"}));

This solution requires that initializer_list be destroyed when foo() returns. The moment when it is destroy is implementation-defined:

It is implementation-defined whether the lifetime of a parameter ends when the function in which it is defined returns or at the end of the enclosing full-expression.

Thanks NathanOliver for pointing that out.


Edit. After the discussion that followed this question, it seems that the code above can easily be fixed by passing the initializer_list by reference, not by value, i.e.:

auto wrapper(const std::initializer_list<const char*>& lst) {
    return lst.begin();
}

foo(3, wrapper({"one", "two", "three"}));

Now the temporary initializer_list is guaranteed to exist until foo() returns:

Temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created.

Thanks cdhowie for clarifications.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.