21

I'm coding in C++, and I have the following code:

int array[30];
array[9] = 1;
array[5] = 1;
array[14] = 1;

array[8] = 2;
array[15] = 2;
array[23] = 2;
array[12] = 2;
//...

Is there a way to initialize the array similar to the following?

int array[30];
array[9,5,14] = 1;
array[8,15,23,12] = 2;
//...

Note: In the actual code, there can be up to 30 slots that need to be set to one value.

aaronman
  • 18,343
  • 7
  • 63
  • 78
Matthew D. Scholefield
  • 2,977
  • 3
  • 31
  • 42
  • if they aren't contiguous it doesn't get much better than this, you could write a function that takes the array and the indicies – aaronman Oct 04 '13 at 00:40
  • There's a GCC extension for this I think. Other than that, there's always `array[9] = array[5] = array[14] = 1;` I guess. – chris Oct 04 '13 at 00:41
  • you can also do `int number[3] = { 5, 7, 2 };` – RamonBoza Oct 24 '13 at 07:34

10 Answers10

32

This function will help make it less painful.

void initialize(int * arr, std::initializer_list<std::size_t> list, int value) {
    for (auto i : list) {
        arr[i] = value;
    }
}

Call it like this.

initialize(array,{9,5,14},2);
Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
aaronman
  • 18,343
  • 7
  • 63
  • 78
9

A variant of aaronman's answer:

template <typename T>
void initialize(T array[], const T& value)
{
}

template <size_t index, size_t... indices, typename T>
void initialize(T array[], const T& value)
{
    array[index] = value;
    initialize<indices...>(array, value);
}

int main()
{
    int array[10];

    initialize<0,3,6>(array, 99);

    std::cout << array[0] << " " << array[3] << " " << array[6] << std::endl;
}

Example: Click here

  • 1
    I like this one best for simplicity, altho coding mine was fun ;) – goji Oct 04 '13 at 06:14
  • 1
    I like this one the best. Due to the way template expansion works, it's closest to the 'manual' way. Since it's all compiler time indexing, there is even the possibility of `memset` being used for larger contiguous chunks of identical values. – KitsuneYMG Oct 04 '13 at 14:17
7

Just for the fun of it I created a somewhat different approach which needs a bit of infrastructure allowing initialization like so:

double array[40] = {};
"9 5 14"_idx(array) = 1;
"8 15 23 12"_idx(array) = 2;

If the digits need to be separated by commas, there is a small change needed. In any case, here is the complete code:

#include <algorithm>
#include <iostream>
#include <sstream>
#include <iterator>

template <int Size, typename T = int>
class assign
{
    int  d_indices[Size];
    int* d_end;
    T*   d_array;
    void operator=(assign const&) = delete;
public:
    assign(char const* base, std::size_t n)
        : d_end(std::copy(std::istream_iterator<int>(
                      std::istringstream(std::string(base, n)) >> std::skipws),
                          std::istream_iterator<int>(), this->d_indices))
        , d_array()
    {
    }
    assign(assign<Size>* as, T* a)
        : d_end(std::copy(as->begin(), as->end(), this->d_indices))
        , d_array(a) {
    }
    assign(assign const& o)
        : d_end(std::copy(o.begin(), o.end(), this->d_indices))
        , d_array(o.d_array)
    {
    }
    int const* begin() const { return this->d_indices; }
    int const* end() const   { return this->d_end; }
    template <typename A>
    assign<Size, A> operator()(A* array) {
        return assign<Size, A>(this, array);
    }
    void operator=(T const& value) {
        for (auto it(this->begin()), end(this->end()); it != end; ++it) {
            d_array[*it] = value;
        }
    }
};

assign<30> operator""_idx(char const* base, std::size_t n)
{
    return assign<30>(base, n);
}

int main()
{
    double array[40] = {};
    "1 3 5"_idx(array) = 17;
    "4 18 7"_idx(array) = 19;
    std::copy(std::begin(array), std::end(array),
              std::ostream_iterator<double>(std::cout, " "));
    std::cout << "\n";
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Although this would not be as readably, it would definitely save space. – Matthew D. Scholefield Oct 04 '13 at 01:29
  • 6
    This is slightly disturbing +1 – aaronman Oct 04 '13 at 01:33
  • Lol, yes, this it crazy. One question: Would this be more efficient (At running the program, not typing), and if so, by how much? – Matthew D. Scholefield Oct 04 '13 at 01:35
  • @Dietmar Kühl Interesting. I must play around with the literal operator sometime. Can you see any problems with my answer? – goji Oct 04 '13 at 02:10
  • @RandomPerson323: The implementation in the answer uses a fairly heavy-weight run-time approach to parsing the string. It could be done at compile-time, emitting constant expression, I think but it wouldn't necessarily be faster. Personally, I'm not quite satisfied with the notation and my main reason for posting it was to show an alternative which may lead to something useful. – Dietmar Kühl Oct 04 '13 at 20:02
5

I just had a play around for the sake of fun / experimentation (Note my concerns at the bottom of the answer):

It's used like this:

smartAssign(array)[0][8]       = 1;
smartAssign(array)[1][4][2]    = 2;
smartAssign(array)[3]          = 3;
smartAssign(array)[5][9][6][7] = 4;

Source code:

#include <assert.h> //Needed to test variables
#include <iostream>
#include <cstddef>

template <class ArrayPtr, class Value>
class SmartAssign
{
    ArrayPtr m_array;

public:
    class Proxy
    {
        ArrayPtr m_array;
        size_t m_index;
        Proxy* m_prev;

        Proxy(ArrayPtr array, size_t index)
            : m_array(array)
            , m_index(index)
            , m_prev(nullptr)
        { }

        Proxy(Proxy* prev, size_t index)
            : m_array(prev->m_array)
            , m_index(index)
            , m_prev(prev)
        { }

        void assign(Value value)
        {
            m_array[m_index] = value;            
            for (auto prev = m_prev; prev; prev = prev->m_prev) {
                m_array[prev->m_index] = value;
            }
        }

    public:
        void operator=(Value value)
        {
            assign(value);
        }

        Proxy operator[](size_t index)
        {
          return Proxy{this, index};
        }

        friend class SmartAssign;
    };

    SmartAssign(ArrayPtr array)
        : m_array(array)
    {
    }


    Proxy operator[](size_t index)
    {
        return Proxy{m_array, index};
    }
};

template <class T>
SmartAssign<T*, T> smartAssign(T* array)
{
    return SmartAssign<T*, T>(array);
}

int main()
{
    int array[10];

    smartAssign(array)[0][8]       = 1;
    smartAssign(array)[1][4][2]    = 2;
    smartAssign(array)[3]          = 3;
    smartAssign(array)[5][9][6][7] = 4;

    for (auto i : array) {
        std::cout << i << "\n";
    }

    //Now to test the variables
    assert(array[0] == 1 && array[8] == 1);
    assert(array[1] == 2 && array[4] == 2 && array[2] == 2);
    assert(array[3] == 3);
    assert(array[5] == 4 && array[9] == 4 && array[6] == 4 && array[7] == 4);
}

Let me know what you think, I don't typically write much code like this, I'm sure someone will point out some problems somewhere ;)

I'm not a 100% certain of the lifetime of the proxy objects.

Matthew D. Scholefield
  • 2,977
  • 3
  • 31
  • 42
goji
  • 6,911
  • 3
  • 42
  • 59
2

The best you can do if your indexes are unrelated is "chaining" the assignments:

array[9] = array[5] = array[14] = 1;

However if you have some way to compute your indexes in a deterministic way you could use a loop:

for (size_t i = 0; i < 3; ++i)
    array[transform_into_index(i)] = 1;

This last example also obviously applies if you have some container where your indexes are stored. So you could well do something like this:

const std::vector<size_t> indexes = { 9, 5, 14 };
for (auto i: indexes)
    array[i] = 1;
syam
  • 14,701
  • 3
  • 41
  • 65
  • With only 30 values, I'm not sure if the overhead of calling a function transform_into_index() is worth a few less lines of code. Performance hit for nothing. – Tommy Oct 04 '13 at 00:46
  • 3
    In C++, a function call is often not a performance hit at all. Also, premature optimization is the root of all evil. – aschepler Oct 04 '13 at 00:49
  • 1
    @Tommy I for one am not sure the performance hit is a reason enough to write less readable code, especially since the function might well be inlined (and thus the call costs nothing ; the loop may in fact be unrolled and all indexes computed at compile-time, think `constexpr`). – syam Oct 04 '13 at 00:50
  • But the code is not unreadable. Perhaps sorting the statements so that array[1] =, array[2]=... this is called loop unrolling and has advantages. Simple > Complicated. I stress again that if there were 300 values or something, then a loop would most definitely be needed. – Tommy Oct 04 '13 at 00:52
  • @Tommy Well, "unreadable" is a matter of opinion. Repeating the same variable name 30 times is definitely unreadable (rather unmaintainable, actually) in my book. – syam Oct 04 '13 at 00:53
  • From reading the values op posted, there does not appear to be any easy pattern of the indices ( I tried to find a simple transformation), which is why I suspect a performance hit because transform_into_index() is going to be complicated. Or impossible =). – Tommy Oct 04 '13 at 00:58
2

Compilers which still doesn't support variadic template argument and universal initialization list, it can be a pain to realize, that some of the posted solution will not work

As it seems, OP only intends to work with arrays of numbers, valarray with variable arguments can actually solve this problem quite easily.

#include <valarray>     
#include <cstdarg>
#include <iostream>
#include <algorithm>
#include <iterator>
template <std::size_t size >
std::valarray<std::size_t>  selection( ... )
{
    va_list arguments; 
    std::valarray<std::size_t> sel(size);   
    //Skip the first element
    va_start ( arguments, size );
    va_arg ( arguments, int );
    for(auto &elem : sel)
        elem = va_arg ( arguments, int );
    va_end ( arguments );
    return sel;

}
int main ()
{
    //Create an array of 30 integers
    std::valarray<int> array(30);
    //The first argument is the count of indexes
    //followed by the indexes of the array to initialize
    array[selection<3>(9,5,14)] = 1;
    array[selection<4>(8,15,13, 12)] = 2;
    std::copy(std::begin(array), std::end(array),
              std::ostream_iterator<int>(std::cout, " "));
    return 0;
}
tolik518
  • 119
  • 2
  • 14
Abhijit
  • 62,056
  • 18
  • 131
  • 204
  • I liked this one! (though i hate `va_lists`) but i've tested the code on ideone ( http://ideone.com/tCma9B ) and doesn't works: it doesn't initialize the first index passed on selection. – PaperBirdMaster Oct 04 '13 at 07:39
  • @PaperBirdMaster: The first index is the length rather than the Index, so it will not be initialized. As you might recollect, the way argument passing works, there should be someway for the recipient to know what is the data type and no of data passed. The first number in the list actually does it. – Abhijit Oct 04 '13 at 10:31
  • @PaperBirdMaster: I understand your predicament in using `va_list`, but unless we have something better `initialization list` or `variadic template`, that is the only viable approach. – Abhijit Oct 04 '13 at 10:32
  • oh! first number is the lenght, sorry for the misunderstanding :O – PaperBirdMaster Oct 04 '13 at 10:36
  • So, is `std::valarray array(30);` to create the array? – Matthew D. Scholefield Oct 04 '13 at 11:18
  • @RandomPerson323: Yes, that will create an array of 30 integers – Abhijit Oct 04 '13 at 11:25
  • OK, when I get the time I will look into this. – Matthew D. Scholefield Oct 04 '13 at 11:35
  • @PaperBirdMaster: I have modified the code, to remove the dependency on the length argument – Abhijit Oct 04 '13 at 11:40
  • @RandomPerson323: And my guess is, this solution would be comparable in terms of efficiency to the already posted once. – Abhijit Oct 04 '13 at 11:45
1

I remember, for static initialization exist syntax like:

int array[30] = {
  [9] = 1, [8] = 2
}

And so on. This works in gcc, about another compilers - I do not know.

olegarch
  • 3,670
  • 1
  • 20
  • 19
  • 1
    This seems like it would be simpler, except sadly, the compiler I'm using gives me the error: `sorry, unimplemented: non-trivial designated initializers not supported`. – Matthew D. Scholefield Oct 04 '13 at 00:53
1

Use overload operator << .

#include <iostream>
#include <iomanip>
#include <cmath>

// value and indexes wrapper
template< typename T,  std::size_t ... Ints> struct _s{ T value; };

//deduced value type
template< std::size_t ... Ints,  typename T>
constexpr inline   _s<T, Ints... >  _ ( T const& v )noexcept { return {v}; }


// stored array reference
template< typename T, std::size_t N>
struct _ref
{
    using array_ref = T (&)[N];

    array_ref ref;
};


//join _s and _ref with << operator.
template< 
        template< typename , std::size_t ... > class IC, 
        typename U, std::size_t N, std::size_t ... indexes
        >
constexpr _ref<U,N> operator << (_ref<U,N> r, IC<U, indexes...> ic ) noexcept
{
    using list = bool[];
    return (  (void)list{ false, (  (void)(r.ref[indexes] = ic.value), false) ... }) , r ;

    //return r;
}


//helper function, for creating _ref<T,N> from array.
template< typename T, std::size_t N>
constexpr inline _ref<T,N> _i(T (&array)[N] ) noexcept { return {array}; }



int main()
{

   int a[15] = {0};

   _i(a) << _<0,3,4,5>(7) << _<8,9, 14>( 6 ) ;


   for(auto x : a)std::cout << x << "  " ;  
   //       0  1  2  3  4  5  6  7  8  9 10 11 12 13 14
  //result: 7  0  0  7  7  7  0  0  6  6  0  0  0  0  6


  double b[101]{0};

  _i(b) << _<0,10,20,30,40,50,60,70,80,90>(3.14) 
        << _<11,21,22,23,24,25>(2.71) 
        << _<5,15,25,45,95>(1.414) ;
}
Khurshid Normuradov
  • 1,564
  • 1
  • 13
  • 20
  • Although this would be slightly less readable (because of the underscore syntax), how wold it compare speed-wise to aarman's answer? – Matthew D. Scholefield Oct 04 '13 at 11:08
  • I agree about less readable. aarman's answer is good, but it can't check array bound. My implement can't check array bound,too, but it can give warning at compile time. – Khurshid Normuradov Oct 04 '13 at 13:26
1
struct _i_t
{
    int * array;


    struct s
    {
        int* array;
        std::initializer_list<int> l;

        s const&   operator = (int value) const noexcept
        {
            for(auto i : l )
              array[i] = value;

            return *this;
        }
    };

    s operator []( std::initializer_list<int> i ) const noexcept
    {
        return s{array, i};
    }
};

template< std::size_t N>
constexpr _i_t   _i( int(&array)[N]) noexcept { return {array}; }

int main()
{
  int a[15] = {0};
  _i(a)[{1,3,5,7,9}] =  7;

  for(auto x : a)std::cout << x << ' ';


}
Khurshid Normuradov
  • 1,564
  • 1
  • 13
  • 20
0

Any fancy trickery you do will be unrolled by the compiler/assembler into exactly what you have. Are you doing this for readability reasons? If your array is already init, you can do:

array[8] = array[15] = array[23] = array[12] = 2;

But I stress my point above; it will be transformed into exactly what you have.

Tommy
  • 12,588
  • 14
  • 59
  • 110