Custom Container emplace with variadic templates - c++

I am implementing a simple circular vector class. I want to implement an emplace member function, but I am getting an error which I don't understand. It might be a simple fix for something I am doing wrong, but since I don't have much experience with variadic templates, I can't figure out what...
The error I am getting is:
main.cpp: In function 'int main()':
main.cpp:104:50: error: no matching function for call to 'CircularVector<Item>::emplace(int, int, int, int, int, int, std::vector<int>, int)'
v.emplace(1, 0, 1, 0, 1, 0, vector<int>(), -1);
^
main.cpp:104:50: note: candidate is:
main.cpp:20:7: note: void CircularVector<T, Args>::emplace(const Args& ...) [with T = Item; Args = {}]
void emplace(const Args &... args) {
^
main.cpp:20:7: note: candidate expects 0 arguments, 8 provided
The source code generating this error is (also located here http://coliru.stacked-crooked.com/a/37d50d6f23363357):
#include <vector>
using namespace std;
#define CIRCULAR_BUFFER_DEFAULT_SIZE 5000
template <typename T, typename ...Args>
class CircularVector {
public:
CircularVector(int size) {
_size = size;
_v.reserve(_size);
}
CircularVector() {
_size = CIRCULAR_BUFFER_DEFAULT_SIZE;
_v.reserve(_size);
}
void emplace(const Args &... args) {
++Count;
++_indexWrite;
if (_indexWrite > _size - 1) _indexWrite = 0;
_v.emplace(_indexWrite, args...);
}
void push(const T& item) {
++Count;
++_indexWrite;
if (_indexWrite > _size - 1) _indexWrite = 0;
_v[_indexWrite] = item;
}
void pop(T& item) {
item = _v[_indexRead];
++_indexRead;
if (_indexRead > _size - 1) _indexRead = 0;
--Count;
}
T& back() {
return _v[(_indexRead + Count - 1) % _size];
}
void erase(int numItems) {
_indexRead += numItems;
if (_indexRead > _size - 1) _indexRead -= _size;
Count -= numItems;
}
void eraseAt(int index) {
swap(_v[index], _v[(_indexRead + Count - 1) % _size]);
--Count;
--_indexWrite;
if (_indexWrite < 0) {
_indexWrite = _size - 1;
}
}
void clear() {
_indexRead = 0;
_indexWrite = -1;
Count = 0;
}
T& operator[](std::size_t idx) {
int index = _indexRead + idx;
if (index > _size) index = index % _size;
return _v[index];
};
int Count = 0;
private:
int _indexWrite = -1;
int _indexRead = 0;
int _size = 0;
std::vector<T> _v;
};
class Item {
public:
double A;
int B;
int C;
vector<int> D;
int E;
Item(double a, int b, int c, vector<int> &d, int e) {
A = a;
B = b;
C = c;
D = d;
E = e;
}
};
int main() {
CircularVector<Item> v;
v.emplace(1, 0, 1, 0, 1, 0, vector<int>(), -1);
}

In case anyone stumbles upon the same issue, this is how I achieved this:
void emplace(Args&&... args) {
++Count;
++_indexWrite;
if (_indexWrite > _size - 1) _indexWrite = 0;
_v.emplace(_v.begin() + _indexWrite, std::forward<Args>(args)...);
}
Although what I really wanted was to construct an element using the reserved memory in that index, and not inserting a new element at that specific position.

Args has to be a template parameter of emplace, not CircularVector.
template <typename T>
class CircularVector {
public:
/* ... */
template<typename ...Args>
void emplace(const Args &... args) {
/* ... */
}
};
Also, you should consider using forwarding references for emplace.

Related

How to combine two codes into one function?

My code creates arrays, I need to implement deletions for it, but I don’t know how to do it beautifully and correctly.
main code
template<class T1>
auto auto_vector(T1&& _Size) {
return new int64_t[_Size]{};
}
template <class T1, class... T2>
auto auto_vector(T1&& _Size, T2&&... _Last)
{
auto result = new decltype(auto_vector(_Last...))[_Size];
for (int64_t i = 0; i < _Size; ++i) {
result[i] = auto_vector(_Last...);
}
return result;
}
this is the code that I want to combine with the first
template <class T1, class T2, class T3, class T4>
void del_vector(T4& _Del, T1& _Size, T2& _Size2, T3& _Size3) {
for (ptrdiff_t i = 0; i < _Size3; ++i) {
for (ptrdiff_t k = 0; k < _Size2; ++k) {
delete _Del[i][k];
}
delete _Del[i];
}
delete _Del;
}
int main()
{
auto map1 = auto_vector(_Size, _Size2, _Size3);
auto map2 = auto_vector(3, 4, 5, 7);
del_vector(map1, _Size, _Size2, _Size3);
return 0;
}
I do not like this option I would like something like that.
int main()
{
auto_vector map1(_Size, _Size2, _Size3);
del_vector map1(_Size, _Size2, _Size3);
//or so
auto_vector<_Size, _Size2, _Size3> map1;
del_vector<_Size, _Size2, _Size3> map1;
return 0;
}
the reason why I do this is because I cannot implement the same thing using just a vector
and I don’t understand why the vector does not work with dynamic arrays, the fact is that I do not know the exact data
_Size, _Size2, _Size3 = ? before compilation.
therefore I use new and all this I do only for his sake.
if it is useful to you to look at the data for tests
cout << " ---------TEST---------- " << endl;
for (ptrdiff_t i = 0; i < _Size3; ++i) {
for (ptrdiff_t k = 0; k < _Size2; ++k) {
for (ptrdiff_t j = 0; j < _Size; ++j) {
cout << map1[i][k][j] << " ";
}
cout << endl;
}
cout << endl;
}
cout << " ---------TEST---------- " << endl;
You have too many new operations in the code. Also del_vector doesn't make any sense in your preferred version as any decent class will deallocate its data in the destructor (lest it has no ownership over it).
What you need is to make a class or a template class that wraps things up.
#include <iostream>
#include <vector>
#include <array>
#include <type_traits>
using namespace std;
template<size_t index, size_t dim>
void ModifyArray(std::array<size_t,dim>& lcAarray){}
template<size_t index, size_t dim, typename... Args>
void ModifyArray(std::array<size_t, dim>& lcAarray, size_t arg, Args... args)
{
lcAarray[index] = arg;
ModifyArray<index+1>(lcAarray, args...);
}
template<typename... Args>
std::array<size_t, sizeof...(Args)> MakeArray(Args... args)
{
std::array<size_t, sizeof...(Args)> lclArray;
ModifyArray<0>(lclArray, args...);
return lclArray;
}
template< std::size_t dim >
class myMultiArray;
template<std::size_t dim, std::size_t crDim>
class MyMultiArrayIterator
{
public:
MyMultiArrayIterator(myMultiArray<dim>* multiArray, size_t index):
m_pMultiArray(multiArray),
m_index(index)
{}
template<size_t newDim = crDim+1, typename std::enable_if<newDim < dim, int>::type = 0>
MyMultiArrayIterator<dim, newDim> operator [] (size_t idx)
{
return MyMultiArrayIterator<dim, newDim>(m_pMultiArray, m_index + idx*m_pMultiArray->GetStep(crDim));
}
template<size_t newDim = crDim+1, typename std::enable_if<newDim == dim, int>::type = 0>
int& operator [] (size_t idx)
{
return m_pMultiArray->GetValue(m_index+idx*m_pMultiArray->GetStep(crDim));
}
private:
size_t m_index;
myMultiArray<dim>* m_pMultiArray;
};
template< std::size_t dim >
class myMultiArray
{
public:
myMultiArray() = default;
template<class... Args, typename std::enable_if<sizeof...(Args) == dim-1, int>::type = 0>
myMultiArray(size_t size0, Args... args)
{
m_sizes = MakeArray(size0, args...);
std::size_t uTotalSize = 1;
for (std::size_t i = 0; i < dim; i++)
{
m_steps[i] = uTotalSize;
uTotalSize *= m_sizes[i];
}
std::cout << uTotalSize << "\n";
m_data.resize(uTotalSize);
}
// resizes m_data to multiplication of sizes
int operator () (std::array < std::size_t, dim > indexes) const
{
return m_data[computeIndex(indexes)];
}
int &operator () (std::array < std::size_t, dim > indexes)
{
return m_data[computeIndex(indexes)];
}
// modify operator
// you'll probably need more utility functions for such a multi dimensional array
int GetValue(size_t index) const
{
return m_data[index];
}
int &GetValue(size_t index)
{
return m_data[index];
}
size_t GetSize(size_t index) const
{
return m_sizes[index];
}
size_t GetStep(size_t index) const
{
return m_steps[index];
}
MyMultiArrayIterator<dim, 1> operator [] (size_t index)
{
return MyMultiArrayIterator<dim, 1>(this, index*m_steps[0]);
}
private:
size_t computeIndex(std::array < std::size_t, dim > indexes)
{
size_t location = 0;
for(size_t i=0; i< dim; i++)
{
location += m_steps[i]*indexes[i];
}
return location;
}
private:
std::vector < int > m_data;
std::array < std::size_t, dim > m_sizes;
std::array < std::size_t, dim > m_steps;
};
template<typename... Args>
myMultiArray<sizeof...(Args)> MakeMyMultiArray(Args... args)
{
return myMultiArray<sizeof...(Args)>(args...);
}
int main ()
{
auto mapMA = MakeMyMultiArray(3,4,5);
mapMA({2ull,3ull,4ull}) = 7;
std::cout << mapMA({{2ull,3ull,4ull}}) << "\n";
std::cout << mapMA[2][3][4];
return 0;
}

Create a constexpr C string from concatenation of a some string literal and an int template parameter

I have a class with an int template parameter. Under some circumstances I want it to output an error message. This message should be a concatenated string from some fixed text and the template parameters. For performance reasons I'd like to avoid building up this string at runtime each time the error occurs and theoretically both, the string literal and the template parameter are known at compiletime. So I'm looking for a possibility to declare it as a constexpr.
Code example:
template<int size>
class MyClass
{
void onError()
{
// obviously won't work but expressing the concatenation like
// it would be done with a std::string for clarification
constexpr char errMsg[] = "Error in MyClass of size " + std::to_string (size) + ": Detailed error description\n";
outputErrorMessage (errMsg);
}
}
Using static const would allow to compute it only once (but at runtime):
template<int size>
class MyClass
{
void onError()
{
static const std::string = "Error in MyClass of size "
+ std::to_string(size)
+ ": Detailed error description\n";
outputErrorMessage(errMsg);
}
};
If you really want to have that string at compile time, you might use std::array, something like:
template <std::size_t N>
constexpr std::size_t count_digit() {
if (N == 0) {
return 1;
}
std::size_t res = 0;
for (int i = N; i; i /= 10) {
++res;
}
return res;
}
template <std::size_t N>
constexpr auto to_char_array()
{
constexpr auto digit_count = count_digit<N>();
std::array<char, digit_count> res{};
auto n = N;
for (std::size_t i = 0; i != digit_count; ++i) {
res[digit_count - 1 - i] = static_cast<char>('0' + n % 10);
n /= 10;
}
return res;
}
template <std::size_t N>
constexpr std::array<char, N - 1> to_array(const char (&a)[N])
{
std::array<char, N - 1> res{};
for (std::size_t i = 0; i != N - 1; ++i) {
res[i] = a[i];
}
return res;
}
template <std::size_t ...Ns>
constexpr std::array<char, (Ns + ...)> concat(const std::array<char, Ns>&... as)
{
std::array<char, (Ns + ...)> res{};
std::size_t i = 0;
auto l = [&](const auto& a) { for (auto c : a) {res[i++] = c;} };
(l(as), ...);
return res;
}
And finally:
template<int size>
class MyClass
{
public:
void onError()
{
constexpr auto errMsg = concat(to_array("Error in MyClass of size "),
to_char_array<size>(),
to_array(": Detailed error description\n"),
std::array<char, 1>{{0}});
std::cout << errMsg.data();
}
};
Demo
Here's my solution. Tested on godbolt:
#include <string_view>
#include <array>
#include <algorithm>
void outputErrorMessage(std::string_view s);
template<int N> struct cint
{
constexpr int value() const { return N; }
};
struct concat_op {};
template<std::size_t N>
struct fixed_string
{
constexpr static std::size_t length() { return N; }
constexpr static std::size_t capacity() { return N + 1; }
template<std::size_t L, std::size_t R>
constexpr fixed_string(concat_op, fixed_string<L> l, fixed_string<R> r)
: fixed_string()
{
static_assert(L + R == N);
overwrite(0, l.data(), L);
overwrite(L, r.data(), R);
}
constexpr fixed_string()
: buffer_ { 0 }
{
}
constexpr fixed_string(const char (&source)[N + 1])
: fixed_string()
{
do_copy(source, buffer_.data());
}
static constexpr void do_copy(const char (&source)[N + 1], char* dest)
{
for(std::size_t i = 0 ; i < capacity() ; ++i)
dest[i] = source[i];
}
constexpr const char* data() const
{
return buffer_.data();
}
constexpr const char* data()
{
return buffer_.data();
}
constexpr void overwrite(std::size_t where, const char* source, std::size_t len)
{
auto dest = buffer_.data() + where;
while(len--)
*dest++ = *source++;
}
operator std::string_view() const
{
return { buffer_.data(), N };
}
std::array<char, capacity()> buffer_;
};
template<std::size_t N> fixed_string(const char (&)[N]) -> fixed_string<N - 1>;
template<std::size_t L, std::size_t R>
constexpr auto operator+(fixed_string<L> l, fixed_string<R> r) -> fixed_string<L + R>
{
auto result = fixed_string<L + R>(concat_op(), l , r);
return result;
};
template<int N>
constexpr auto to_string()
{
auto log10 = []
{
if constexpr (N < 10)
return 1;
else if constexpr(N < 100)
return 2;
else if constexpr(N < 1000)
return 3;
else
return 4;
// etc
};
constexpr auto len = log10();
auto result = fixed_string<len>();
auto pow10 = [](int n, int x)
{
if (x == 0)
return 1;
else while(x--)
n *= 10;
return n;
};
auto to_char = [](int n)
{
return '0' + char(n);
};
int n = N;
for (int i = 0 ; i < len ; ++i)
{
auto pow = pow10(10, i);
auto digit = to_char(n % 10);
if (n == 0 && i != 0) digit = ' ';
result.buffer_[len - i - 1] = digit;
n /= 10;
}
return result;
}
template<int size>
struct MyClass
{
void onError()
{
// obviously won't work but expressing the concatenation like
// it would be done with a std::string for clarification
static const auto errMsg = fixed_string("Error in MyClass of size ") + to_string<size>() + fixed_string(": Detailed error description\n");
outputErrorMessage (errMsg);
}
};
int main()
{
auto x = MyClass<10>();
x.onError();
}
Results in the following code:
main:
sub rsp, 8
mov edi, 56
mov esi, OFFSET FLAT:MyClass<10>::onError()::errMsg
call outputErrorMessage(std::basic_string_view<char, std::char_traits<char> >)
xor eax, eax
add rsp, 8
ret
https://godbolt.org/z/LTgn4F
Update:
The call to pow10 is not necessary. It's dead code which can be removed.
Unfortunately your options are limited. C++ doesn't permit string literals to be used for template arguments and, even if it did, literal concatenation happens in the preprocessor, before templates come into it. You would need some ghastly character-by-character array definition and some manual int-to-char conversion. Horrible enough that I can't bring myself to make an attempt, and horrible enough that to be honest I'd recommend not bothering. I'd generate it at runtime, albeit only once (you can make errMsg a function-static std::string at least).

Trying to look at the values inside the Ullmanset and priority queue

I would like to dump the content inside the object tmp_np which is a UllmanSet which if you know a key then you know the position and if you know the position , you know the key. Is there a standard C++ container that's similar to the Ullmanset. Ullmanset 's index starts at 0 and Ullmanset1's index start at 1.
and also dump the content of frontierq which is a PriorityQ class.
UllmanSet tmp_np;
template<unsigned B>
class BasedUllmanSet
{
size_t n;
std::vector<int> key;
BasedVector<unsigned, B> pos;
public:
BasedUllmanSet()
: n(0)
{}
BasedUllmanSet(size_t cap)
: n(0), key(cap), pos(cap)
{}
size_t capacity() const { return key.size(); }
size_t size() const { return n; }
bool empty() const { return n == 0; }
void clear() { n = 0; }
void resize(size_t cap)
{
key.resize(cap);
pos.resize(cap);
n = 0;
}
bool contains(int k) const
{
unsigned p = pos[k];
return (p < n
&& key[p] == k);
}
void insert(int k)
{
if (contains(k))
return;
unsigned p = n++;
key[p] = k;
pos[k] = p;
}
void extend(int k)
{
assert(!contains(k));
unsigned p = n++;
key[p] = k;
pos[k] = p;
}
void erase(int k)
{
if (!contains(k))
return;
unsigned p = pos[k];
--n;
if (p != n)
{
int ell = key[n];
pos[ell] = p;
key[p] = ell;
}
}
int ith(int i)
{
assert(i >= 0 && i < (int)n);
return key[i];
}
};
using UllmanSet = BasedUllmanSet<0>;
using UllmanSet1 = BasedUllmanSet<1>;
The priority queue is implemented as followed. I like to print out the values inside the queue using std::cout.
PriorityQ<std::pair<int, int>, Comp> frontierq;
class Comp
{
public:
Comp() {}
bool operator()(const std::pair<int, int> &lhs,
const std::pair<int, int> &rhs) const
{
return (lhs.second > rhs.second
|| (lhs.second == rhs.second
&& lhs.first > rhs.first));
}
};
class PriorityQ
{
public:
Comp comp;
std::vector<T> v;
unsigned n;
public:
PriorityQ() : n(0) {}
PriorityQ(Comp comp_) : comp(comp_), n(0) {}
size_t size() const { return n; }
void clear() { n = 0; }
bool empty() { return n == 0; }
void push(const T &x)
{
assert(v.size() >= n);
if (v.size() == n)
v.push_back(x);
else
v[n] = x;
++n;
std::push_heap(&v[0], &v[n], comp);
}
const T &pop()
{
assert(n > 0);
std::pop_heap(&v[0], &v[n], comp);
--n;
return v[n];
}
const T &top()
{
assert(n > 0);
return v[0];
}
};
C++ standard containers do get this esoteric. If you have another question about the priority queue you should make another post.

using std::unique_ptr with allocators

I was trying my hand with allocators this time & felt that there were many chances of leaking the resources. So I thought what if I used std::unique_ptr to handle them. I tried my hand with a std::vector's allocator. My code goes like this :-
// allocator
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class X
{
int x,ID;
static int i;
public:
X()
{
cout<<"constructing ";
ID=++i;
cout<<"ID="<<ID<<'\n';
}
X(int a)
{
x=a;
cout<<"constructing ";
ID=++i;
cout<<"ID="<<ID<<'\n';
}
void get()
{
cout<<"enter x: ";
cin>>x;
}
void disp()
{
cout<<"x="<<x<<'\t';
}
~X()
{
cout<<"destroying ID="<<ID<<'\n';
}
};
int X:: i=0;
int main()
{
ios::sync_with_stdio(false);
vector<X> v;
auto alloc = v.get_allocator();
unsigned int i=0;
X *p(alloc.allocate(5));
for (i=0;i<5;++i)
alloc.construct (&p[i], i+1);
unique_ptr<X[]> ptr(p);
cout<<"\nthe elements are:-\n";
for (i=0;i<5;++i)
{
ptr[i].disp();
cout << '\t' << (long long)alloc.address(ptr[i]) << '\n';
}
cout<<"\n";
/*for (i=0;i<5;++i)
alloc.destroy(&p[i]);
deallocate(p,16)*/
return 0;
}
Unfortunately this code crashes showing UB. So what should I do ? How should I manipulate my code so as to make it suited for std::unique_ptr ?
template<typename T>
std::unique_ptr<T[], std::function<void(T *)>> make_T(X *ptr, std::allocator<T> alloc, std::size_t size) {
auto deleter = [](T *p, std::allocator<T> alloc, std::size_t size) {
for (int i = 0; i < size; ++i) {
alloc.destroy(&p[i]);
}
alloc.deallocate(p, sizeof(T) * size);
};
return {ptr, std::bind(deleter, std::placeholders::_1, alloc, size)};
}
int main(int argc, const char * argv[]) {
std::allocator<X> alloc = std::allocator<X>();
X *p = alloc.allocate(5);
for (int i = 0; i < 5; ++i) {
alloc.construct(&p[i], i + 1);
}
auto ptr = make_T(p, alloc, 5);
return 0;
}
Can also write one to construct the objects for you:
template<typename T, typename... Args>
std::unique_ptr<T[], std::function<void(T *)>> make_T_Construct(std::allocator<T> alloc, std::size_t size, Args... args) {
X *ptr = alloc.allocate(size);
for (std::size_t i = 0; i < size; ++i) {
alloc.construct(&ptr[i], std::forward<Args>(args)...);
}
auto deleter = [](T *p, std::allocator<T> alloc, std::size_t size) {
for (std::size_t i = 0; i < size; ++i) {
alloc.destroy(&p[i]);
}
alloc.deallocate(p, sizeof(T) * size);
};
return {ptr, std::bind(deleter, std::placeholders::_1, alloc, size)};
}
int main(int argc, const char * argv[]) {
std::allocator<X> alloc = std::allocator<X>();
auto ptr = make_T_Construct(alloc, 5, 100);
return 0;
}
Edit: To do what you want (tracking allocations), you have to track the memory allocations yourself using a custom allocator..
template<typename T>
struct Allocator
{
typedef T value_type;
Allocator() noexcept {};
template<typename U>
Allocator(const Allocator<U>& other) throw() {};
T* allocate(std::size_t n, const void* hint = 0)
{
T* memory = static_cast<T*>(::operator new(n * (sizeof(T) + sizeof(bool))));
for (std::size_t i = 0; i < n * (sizeof(T) + sizeof(bool)); ++i)
{
*reinterpret_cast<bool*>(reinterpret_cast<char*>(memory) + sizeof(bool)) = false;
}
return memory;
}
void deallocate(T* ptr, std::size_t n)
{
::operator delete(ptr);
}
void construct(T* p, const T& arg)
{
destroy(p);
new(p) T(arg);
*reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool)) = true;
}
template<class U, class... Args>
void construct(U* p, Args&&... args)
{
destroy(p);
::new(p) U(std::forward<Args>(args)...);
*reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool)) = true;
}
void destroy(T* p)
{
if (*reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool))) {
p->~T();
*reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool)) = false;
}
}
template<class U>
void destroy(U* p)
{
if (*reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool))) {
p->~U();
*reinterpret_cast<bool*>(reinterpret_cast<char*>(p) + sizeof(bool)) = false;
}
}
};
template <typename T, typename U>
inline bool operator == (const Allocator<T>&, const Allocator<U>&)
{
return true;
}
template <typename T, typename U>
inline bool operator != (const Allocator<T>& a, const Allocator<U>& b)
{
return !(a == b);
}

Simpler way to set multiple array slots to one value

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.
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);
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
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";
}
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.
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;
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;
}
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.
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) ;
}
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 << ' ';
}
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.