47

If I have a prototype that looks like this:

function(float,float,float,float)

I can pass values like this:

function(1,2,3,4);

So if my prototype is this:

function(float*);

Is there any way I can achieve something like this?

function( {1,2,3,4} );

Just looking for a lazy way to do this without creating a temporary variable, but I can't seem to nail the syntax.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
sinoth
  • 7,602
  • 3
  • 19
  • 22
  • Thanks for the advice. I suppose I expected quick and dirty answers for a quick and dirty question, but your response explored some alternative styles I hadn't thought of. Much appreciated! – sinoth Aug 13 '09 at 01:54

10 Answers10

46

You can do it in C99 (but not ANSI C (C90) or any current variant of C++) with compound literals. See section 6.5.2.5 of the C99 standard for the gory details. Here's an example:

// f is a static array of at least 4 floats
void foo(float f[static 4])
{
   ...
}

int main(void)
{
    foo((float[4]){1.0f, 2.0f, 3.0f, 4.0f});  // OK
    foo((float[5]){1.0f, 2.0f, 3.0f, 4.0f, 5.0f});  // also OK, fifth element is ignored
    foo((float[3]){1.0f, 2.0f, 3.0f});  // error, although the GCC doesn't complain
    return 0;
}

GCC also provides this as an extension to C90. If you compile with -std=gnu90 (the default), -std=c99, or -std=gnu99, it will compile; if you compile with -std=c90, it will not.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • One could obviously also compile with `-std=gnu89` (but not `-std=c89`), as it is synonymous with `-std=gnu90` (and `-std=c90`) (I'm sure you know this Adam, it's more for others in case they didn't :P). – RastaJedi Aug 27 '16 at 04:35
  • This is not good solution, because you will not be the length of the arguments passed. – vincent thorpe Jan 27 '19 at 11:24
19

This is marked both C and C++, so you're gonna get radically different answers.

If you are expecting four parameters, you can do this:

void foo(float f[])
{
    float f0 = f[0];
    float f1 = f[1];
    float f2 = f[2];
    float f3 = f[3];
}

int main(void)
{
    float f[] = {1, 2, 3, 4};
    foo(f);
}

But that is rather unsafe, as you could do this by accident:

void foo(float f[])
{
    float f0 = f[0];
    float f1 = f[1];
    float f2 = f[2];
    float f3 = f[3];
}

int main(void)
{
    float f[] = {1, 2}; // uh-oh
    foo(f);
}

It is usually best to leave them as individual parameters. Since you shouldn't be using raw arrays anyway, you can do this:

#include <cassert>
#include <vector>

void foo(std::vector<float> f)
{
    assert(f.size() == 4);

    float f0 = f[0];
    float f1 = f[1];
    float f2 = f[2];
    float f3 = f[3];
}

int main(void)
{
    float f[] = {1, 2, 3, 4};
    foo(std::vector<float>(f, f + 4)); // be explicit about size

    // assert says you cannot do this:
    foo(std::vector<float>(f, f + 2));
}

An improvement, but not much of one. You could use boost::array, but rather than an error for mismatched size, they are initialized to 0:

#include <boost/array.hpp>

void foo(boost::array<float, 4> f)
{
    float f0 = f[0];
    float f1 = f[1];
    float f2 = f[2];
    float f3 = f[3];
}

int main(void)
{
    boost::array<float, 4> f = {1, 2, 3, 4};
    foo(f);

    boost::array<float, 4> f2 = {1, 2}; // same as = {1, 2, 0, 0}
    foo(f2);
}

This will all be fixed in C++0x, when initializer list constructors are added:

#include <cassert>
#include <vector>

void foo(std::vector<float> f)
{
    assert(f.size() == 4);

    float f0 = f[0];
    float f1 = f[1];
    float f2 = f[2];
    float f3 = f[3];
}

int main(void)
{
    foo({1, 2, 3, 4}); // yay, construct vector from this

    // assert says you cannot do this:
    foo({1, 2});
}

And probably boost::array as well:

#include <boost/array.hpp>

void foo(boost::array<float, 4> f)
{
    float f0 = f[0];
    float f1 = f[1];
    float f2 = f[2];
    float f3 = f[3];
}

int main(void)
{
    foo({1, 2, 3, 4});

    foo({1, 2}); // same as = {1, 2, 0, 0} ..? I'm not sure,
                 // I don't know if they will do the check, if possible.
}
GManNickG
  • 494,350
  • 52
  • 494
  • 543
5

You can create a compound literal:

function ((float[2]){2.0, 4.0});

Although, I'm not sure why you want to go through the trouble. This is not permitted by ISO.

Generally, shortcuts like this should be avoided in favor of readability in all cases; laziness is not a good habit to explore (personal opinion, of course)

ezpz
  • 11,767
  • 6
  • 38
  • 39
  • 1
    I agree about the readability, and in practice this particular function will be passed a memory address and be happy. However, during testing I often want to just plug in some random values. Your snippet does the trick, for better or worse :) Thanks! – sinoth Aug 13 '09 at 01:42
  • 1
    C99 is "ISO" and allows compound literals in the standard. It's just not available on C90, nor portable to C++. – ShadowRanger Jan 31 '20 at 17:38
3

You can technically take reference to array, but you still can't create anonymous initializer list I think.

void func(int (&bla)[4])
{
    int count = sizeof(bla)/sizeof(bla[0]);
    // count == 4
}

int bla[] = {1, 2, 3, 4};
func(bla);

int bla1[] = {1, 2};
func(bla1); // <-- fails

For C++ way, look at boost::assign. Pretty neat way of filling in STL containers.

Eugene
  • 7,180
  • 1
  • 29
  • 36
2

The bad news is that there is no syntax for that. The good news is that this will change with the next official version of the C++ standard (due in the next year or two). The new syntax will look exactly as you describe.

Peter Ruderman
  • 12,241
  • 1
  • 36
  • 58
  • 1
    heh heh, next year or two :] Good to know this will be in C++0x. – sinoth Aug 13 '09 at 01:46
  • I doubt in two years. Beta 10 of Visual studio already has some C++0x features built in, and g++ 4.4 already has initializer lists (http://gcc.gnu.org/projects/cxx0x.html). It was pushed back to 2010, probably first quarter. – GManNickG Aug 13 '09 at 01:58
0

No, you cannot do that. I do not have the standard available here, so I cannot give an exact reference, but the closest thing to what you ask for is string constants, i.e.

function(char *);
function("mystring");

is treated by the compiler as

char * some_pointer = "mystring";
function(char *);
function(some_pointer);

There is no way for other types of variables to be treated this way.

hlovdal
  • 26,565
  • 10
  • 94
  • 165
0

Sadly, it only works with character arrays:

void func2(char arg[]) {
}

int main()
{   
    func2("hello");
    return 0;
}
Asik
  • 21,506
  • 6
  • 72
  • 131
0

you can write a builder class that would allow for about the same syntax

// roughly
template <typename C>
class Builder {
public:
   template <typename T>
   Builder(const T & _data) { C.push_back(_data); }
   template <typename T>
   Builder& operator()(const T & _data) { 
       C.push_back(_data);
       return *this;
    }
   operator const C & () const { return data; }
private:
   C data;

};

this way, you can use the class as

foo( const std::vector & v);

foo( Builder< std::vector >(1)(2)(3)(4) );

George Godik
  • 1,716
  • 1
  • 14
  • 19
0

To add to the fun, you can use templates to make it variable in length.

template<std::size_t N>
int chars(const char(&r)[N]){
    std::cout << N << ": " << r << std::endl;
    return 0;
}

template<std::size_t N>
int floats(const float(&r)[N]){
    std::cout << N << ":";
    for(size_t i = 0; i < N; i++)
        std::cout << " " << r[i];
    std::cout << std::endl;
    return 0;
}

int main(int argc, char ** argv) {
    chars("test");
    floats({1.0f, 2.0f, 3.0f, 4.0f});
    return 0;
}
-1

I asked OpenAI Codex, and it suggested this method:

func((uint8_t *) "\x12\x34\x56\x78\x9a\xbc")

and it works in embedded C90, while the compound literals did not (syntax error near '{', expected 'sizeof').

Not really much benefit compared to creating a local scoped array, though:

{
    uint8 da[6] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
    func(da);
}
endolith
  • 25,479
  • 34
  • 128
  • 192