Related
I have the following two objects. Im wondering if there is a way to have Pixel as a base class of PixelBGR so that any operator (+,-,*,/, [], etc.) could be used without redefining them ?
template<class T, std::size_t N>
struct Pixel
{
T ch[N];
inline T& operator[](const int x)
{
return ch[x];
}
};
template<class T>
struct PixelBGR
{
union
{
struct
{
T b;
T g;
T r;
};
T ch[3];
};
inline T& operator[](const int x)
{
return ch[x];
}
};
EDIT: As suggested by πάντα ῥεῖ, here more details about what Im trying to do.
Im trying to have a generic class Pixel, which will be template to handle any type or size.
The usual are 1,2,3,4,8 or 16. The class with defines some operator such as +,-,*, etc.
Since most of the time, the Pixel<T,3> is a BGR pixel, I would like to define rapid access to r,g and b to avoid confusion, but still store it as BGR.
But the derived class should also provide the Operator which will be generic based on N.
EDIT2: By reading the comment of SergeyA, I forgot to say that the struct Pixel must not change size.
So I think balki answer is the best, by using member function. I was trying to make it with variables to avoid too much char ie: adding the (), but it seems to be too complicated for nothing. I still investigating CRTP, but I dont get it well, Im reading on that.
Answering the question as asked, this should give OP reuse of the operators without any undefined behavior:
#include <cstddef>
template<class T, std::size_t N>
struct Pixel
{
T ch[N];
inline T& operator[](const int x)
{
return ch[x];
}
Pixel& operator+= (const Pixel& ) { return *this;}
};
template<class T, std::size_t N>
Pixel<T, N> operator+ (const Pixel<T, N>& l, const Pixel<T, N>& r);
template<class T>
struct BgrPixel : Pixel<T, 3> {
using base = Pixel<T, 3>;
using base::base;
BgrPixel(const base& b) : base(b) { };
T& b = base::ch[0];
T& g = base::ch[1];
T& r = base::ch[2];
};
BgrPixel<int> a, b;
BgrPixel<int> c = a + b;
Alterinative would be to have b(), g() and r() as a member functions, but this would require you to access them as functions. You would also need const and non-const versions of them.
The benefits, however, would be that the size of the struct will not be increased and copy assignment would work naturally (which, in turn, could be solved by providing custom copy assignment).
The Curiously Recurring Template Pattern (CRTP) would work well in this case. In the CRTP the Derived class is used as a template argument to the Base class. Chapter 16.3 The Curiously Recurring Template Pattern (CRTP), from the C++ Templates - The Complete Guide, by David Vandevoorde and Nicolai M. Josuttis, explains things in more detail.
From the comments below, the usage of a union{struct{...}...} causes undefined behaviour (UB), but there have been some contradicting opinions upon this. As far as I'm aware, it is a gnu extension and supported by almost every compiler. glm for example uses union-structs quite very often.
As an alternative approach, you can use aliases (references) for the r,g,b variables.
#include <iostream>
template<typename T, std::size_t N, template<typename,std::size_t> class B >
struct Pixel
{
B<T,N> *crtp = static_cast<B<T,N>*>(this);
T& operator[](std::size_t x)
{
return crtp->ch[x];
}
Pixel& operator = (const Pixel &t)
{
crtp->ch[0] = t.crtp->ch[0];
crtp->ch[1] = t.crtp->ch[1];
crtp->ch[2] = t.crtp->ch[2];
return *crtp;
}
B<T,N> operator + (const B<T,N> &t)
{
B<T,N> tmp;
tmp[0] = crtp->ch[0] + t.crtp->ch[0];
tmp[1] = crtp->ch[1] + t.crtp->ch[1];
tmp[2] = crtp->ch[2] + t.crtp->ch[2];
return tmp;
}
B<T,N> operator - (const B<T,N> &t)
{
B<T,N> tmp;
tmp[0] = crtp->ch[0] - t.crtp->ch[0];
tmp[1] = crtp->ch[1] - t.crtp->ch[1];
tmp[2] = crtp->ch[2] - t.crtp->ch[2];
return tmp;
}
};
template<typename T, std::size_t N=3>
struct PixelBGR : Pixel<T, N, PixelBGR>
{
T ch[3];
T &r;
T &g;
T &b;
PixelBGR() : ch{},r(ch[0]),g(ch[1]),b(ch[2])
{}
PixelBGR& operator = (const PixelBGR &p)
{
ch[0] = p.ch[0];
ch[1] = p.ch[1];
ch[2] = p.ch[2];
return *this;
}
};
int main()
{
PixelBGR<int> p;
p.r = 25;
p.g = 14;
p.b = 58;
std::cout<< p[0] <<" , "<<p[1]<<" , "<<p[2] <<std::endl;
PixelBGR<int> q;
q = p;
std::cout<< q[0] <<" , "<<q[1]<<" , "<<q[2] <<std::endl;
PixelBGR<int> res1;
res1 = q + p;
std::cout<< res1.r <<" , "<<res1.g<<" , "<<res1.b <<std::endl;
PixelBGR<int> res2;
res2 = q - p;
std::cout<< res2.r <<" , "<<res2.g<<" , "<<res2.b <<std::endl;
}
Result:
25 , 14 , 58
25 , 14 , 58
50 , 28 , 116
0 , 0 , 0
Example using references: https://rextester.com/AZWG4319
Example using union-struct: https://rextester.com/EACC87146
First thanks to all of you for advise, and special thanks to #Constantinos Glynos, #balki and #SergeyA for the example they provide, those help me to achieve a solution that match my need.
I implemented the BGR and BGRA to show that the N works fine, now I just need to implement all the operator, and functions that I require.
Please feel free to edit, or tell me if there is something wrong with this.
Pixel.h
#include <cstdio> // std::size_t
#include <iostream> // std::cout
template<typename T, std::size_t N, template<typename, std::size_t> class B >
struct Pixel
{
T ch[N];
// ==============================================================
// Overload the accessor (so .ch[0] == direct access with [0].
T& operator[](std::size_t x){ return ch[x]; }
// ==============================================================
// Copy-assignement
Pixel& operator=( const Pixel &t )
{
for ( int i = 0; i < N; i++ )
ch[i] = t.ch[i];
return *this;
}
// ==============================================================
// Operator
B<T, N> operator+( const B<T, N> &t )
{
B<T, N> tmp;
for ( int i = 0; i < N; i++ )
tmp[i] = ch[i] + t.ch[i];
return tmp;
}
B<T, N> operator-( const B<T, N> &t )
{
B<T, N> tmp;
for ( int i = 0; i < N; i++ )
tmp[i] = ch[i] - t.ch[i];
return tmp;
}
template<typename T, std::size_t N, template<typename, std::size_t> class B >
friend std::ostream& operator<<( std::ostream& os, const Pixel &t );
};
// To print the vector
template<typename T, std::size_t N, template<typename, std::size_t> class B >
std::ostream& operator<<( std::ostream& os, const B<T, N> &t )
{
os << "Pixel: (" << t.ch[0];
for ( int i = 1; i < N; i++ )
os << ", " << t.ch[i];
os << ")";
return os;
}
template<typename T, std::size_t N = 3>
struct BGR : Pixel<T, N, BGR>
{
T& b() { return ch[0]; }
T& g() { return ch[1]; }
T& r() { return ch[2]; }
};
template<typename T, std::size_t N = 4>
struct BGRA : Pixel<T, N, BGRA>
{
T& b() { return ch[0]; }
T& g() { return ch[1]; }
T& r() { return ch[2]; }
T& a() { return ch[3]; }
};
Main.cpp
int main() {
std::cout << "Sizeof a float BGR: " << sizeof(BGR<float>) << std::endl;
std::cout << "Sizeof a float BGRA: " << sizeof(BGRA<float>) << std::endl;
BGR<int> p;
p.r() = 25;
p.g() = 14;
p.b() = 58;
std::cout << p << std::endl;
std::cout << p[0] << " , " << p[1] << " , " << p[2] << std::endl;
std::cout << p.b() << " , " << p.g() << " , " << p.r() << std::endl;
BGR<int> q;
q = p;
std::cout << q[0] << " , " << q[1] << " , " << q[2] << std::endl;
BGR<int> res1;
res1 = q + p;
std::cout << res1.r() << " , " << res1.g() << " , " << res1.b() << std::endl;
BGR<int> res2;
res2 = q - p;
std::cout << res2.r() << " , " << res2.g() << " , " << res2.b() << std::endl;
BGRA<float> a;
a.r() = 255.0f;
a.g() = 0.0f;
a.b() = 0.0f;
a.a() = 128.5f;
BGRA<float> b = a;
std::cout << a << std::endl;
return 0;
}
Is it possible to create a const array of objects where one member variable is the sum of a member variable in the objects created before it?
class Data
{
public:
constexpr Data(uint32_t offset, uint32_t length) :
m_offset(offset), m_length(length)
{
}
uint32_t m_offset; //would like this to be calculated at compile time
uint32_t m_length;
};
const Data dataList[] =
{
Data(0, 10),
Data(10, 25),
Data(35, 20)
};
offset is the sum of the length of all previous objects in the array (10 + 25 = 35 in index 2).
I'd like to avoid having to manually calculate the offset.
I've played around with std::integral_constant and recursive calls to constexpr, but nothing seems close enough to a working solution to share. Any guidance is much appreciated!
If you accept an answer based on a std::array<Data, ...> instead of a old C-style array, and to use C++14 instead of C++11, it's easy
The following is a full example
#include <array>
#include <iostream>
struct Data
{
constexpr Data(uint32_t offset, uint32_t length) :
m_offset(offset), m_length(length)
{ }
uint32_t m_offset;
uint32_t m_length;
};
template <uint32_t ... Ls>
constexpr std::array<Data, sizeof...(Ls)> getDataList ()
{
uint32_t l0 { 0U };
uint32_t l1 { 0U };
return { { (l0 = l1, l1 += Ls, Data(l0, l1))... } };
}
int main ()
{
constexpr auto dl = getDataList<10U, 25U, 20U>();
for ( auto const & d : dl )
std::cout << " - " << d.m_offset << ", " << d.m_length << std::endl;
}
-- EDIT --
The OP can't use std::array but a C++ function can't return a C-style array; a solution could be simulate a (iper-simplified) version of std::array, that wrap a C-style array in a trivial struct
template <typename T, std::size_t N>
struct myArray
{ T arr[N]; };
Now the full example become
#include <array>
#include <iostream>
template <typename T, std::size_t N>
struct myArray
{ T arr[N]; };
struct Data
{
constexpr Data(uint32_t offset, uint32_t length) :
m_offset(offset), m_length(length)
{ }
uint32_t m_offset;
uint32_t m_length;
};
template <uint32_t ... Ls>
constexpr myArray<Data, sizeof...(Ls)> getDataList ()
{
uint32_t l0 { 0 };
uint32_t l1 { 0 };
return { { (l0 = l1, l1 += Ls, Data(l0, l1))... } };
}
int main ()
{
constexpr auto dl = getDataList<10U, 25U, 20U>();
for ( auto ui = 0U ; ui < 3U ; ++ui )
std::cout << " - " << dl.arr[ui].m_offset << ", "
<< dl.arr[ui].m_length << std::endl;
}
The std::array simulation can be a little less iper-simplified and contain, by example, a static constexpr member with the dimension
template <typename T, std::size_t N>
struct myArray
{ static constexpr std::size_t dim { N }; T arr[dim]; };
so the loop in main() can use it
// ..........................vvv
for ( auto ui = 0U ; ui < dl.dim ; ++ui )
Not sure if it's possible for more later version of c++. (I can't figure out using traditional c++ to achieve the following behaviofgr.)
For example,
If I have an array defined like this:
In the header file
struct Def {
static const int N = 5;
static const double data[N];
};
In its cpp
const double Def::data[Def::N] = {0,1,2,3,4};
Is it possible to have a template get_subarray such that
get_subarray<Def,2,0>::data will be an array of content {0,2,4}
get_subarray<Def,2,1>::data will be an array of content {1,3}
where
template<typename T, int M, int m>
struct get_phase {
// some code for the array variable data which will
// extract element from T::data for every M sample offset by index m
};
As mentioned in the comments, the OP is interested also in C++14 based solutions.
Here is one of them:
#include<functional>
#include<cstddef>
#include<utility>
#include<array>
template<std::size_t O, typename T, std::size_t N, std::size_t... I>
constexpr std::array<T, sizeof...(I)>
f(const std::array<T, N> &arr, std::index_sequence<I...>) {
return { std::get<I+O>(arr)... };
}
template<std::size_t B, std::size_t E, typename T, std::size_t N>
constexpr auto f(const std::array<T, N> &arr) {
return f<B>(arr, std::make_index_sequence<E-B>());
}
int main() {
constexpr std::array<int, 3> a1 = { 0, 1, 2 };
constexpr auto a2 = f<1, 2>(a1);
static_assert(a1[1] == a2[0], "!");
}
In this case, a2 is equal to { 1 }.
It's worth it checking B and E so as to verify that E is greater than B, but the example should give an idea of what's the way to do it.
To port it to C++11:
Do not use auto as return type, but explicitly specify std::array (easy)
Search on the web one of the available C++11 implementations of integer_sequence and make_index_sequence and use it
If it's fine to be explicit about the indexes and not use a range, here is a naïve snippet that should work in C++11:
#include<cstddef>
#include<utility>
#include<array>
template<std::size_t... I, typename T, std::size_t N>
constexpr std::array<T, sizeof...(I)>
f(const std::array<T, N> &arr) {
return { std::get<I>(arr)... };
}
int main() {
constexpr std::array<int, 3> a1 = { 0, 1, 2 };
constexpr auto a2 = f<1>(a1);
static_assert(a1[1] == a2[0], "!");
}
As in the previous example, a2 is { 1 }.
I like the skypjack's solution but it doesn't extract the requested values. skypjack's version has two parameters, "begin" and "end". The OP requested "stride" or "frequency" and "begin".
I've modified it to match the OP's requested "frequency" and "start" arguments, giving the template non-type parameters more self-explaining names, and rewriting a couple of index calculations:
#include<utility>
#include<iostream>
#include<array>
template <std::size_t Freq, std::size_t Start, typename T, std::size_t Dim,
std::size_t... I>
constexpr std::array<T, sizeof...(I)>
extractHelper (const std::array<T, Dim> & arr,
std::integer_sequence<std::size_t, I...>)
{ return { { std::get<Freq*I+Start>(arr)... } }; }
template <std::size_t Freq, std::size_t Start, typename T, std::size_t Dim>
constexpr auto extractSamples (const std::array<T, Dim> & arr)
{ return extractHelper<Freq, Start>
(arr, std::make_index_sequence<(Dim+Freq-1-Start)/Freq>()); }
Here is some test code:
int main()
{
constexpr std::array<int, 8> a1 = { { 0, 1, 2, 3, 4, 5, 6, 7 } };
constexpr auto e1 = extractSamples<2, 0>(a1);
constexpr auto e2 = extractSamples<2, 1>(a1);
constexpr auto e3 = extractSamples<3, 0>(a1);
constexpr auto e4 = extractSamples<3, 1>(a1);
constexpr auto e5 = extractSamples<3, 2>(a1);
std::cout << "samples<2, 0>: ";
for ( auto const & i : e1 )
std::cout << ' ' << i;
std::cout << "\nsamples<2, 1>: ";
for ( auto const & i : e2 )
std::cout << ' ' << i;
std::cout << "\nsamples<3, 0>: ";
for ( auto const & i : e3 )
std::cout << ' ' << i;
std::cout << "\nsamples<3, 1>: ";
for ( auto const & i : e4 )
std::cout << ' ' << i;
std::cout << "\nsamples<3, 2>: ";
for ( auto const & i : e5 )
std::cout << ' ' << i;
std::cout << std::endl;
return 0;
}
The output is:
samples<2, 0>: 0 2 4 6
samples<2, 1>: 1 3 5 7
samples<3, 0>: 0 3 6
samples<3, 1>: 1 4 7
samples<3, 2>: 2 5
which matches the OP's requests
Here's a solution in C++11 without additives. As usual,
compiletime recursion makes do for the lack of C++14's std::index_sequence
- in this case by recursively assembling the list of indices that
select the desired sample of the data array.
Given some:
constexpr std::array<T,N> data{{...}};
initialized ... with the Ts of your choice, then:
constexpr auto sample = get_sample<Stride,Offset>(data);
will define sample as a compiletime
std::array<T,M>
populated with the M elements of data obtained by selecting the element
at offset Offset from the start of successive Stride-sized intervals of
data.
#include <array>
#include <type_traits>
constexpr std::size_t
sample_size(std::size_t size, std::size_t stride, std::size_t off)
{
return stride == 0 ? 0 : ((size - off) / stride) +
(off + (((size - off) / stride) * stride) < size);
}
template<
std::size_t Stride = 1, std::size_t Off = 0,
typename T, std::size_t Size, std::size_t ...Is
>
constexpr typename std::enable_if<
sizeof ...(Is) == sample_size(Size,Stride,Off),
std::array<T, sample_size(Size,Stride,Off)>
>::type
get_sample(std::array<T,Size> const & data)
{
return std::array<T,sample_size(Size,Stride,Off)>{{data[Is]... }};
}
template<
std::size_t Stride = 1, std::size_t Off = 0,
typename T, std::size_t Size, std::size_t ...Is
>
constexpr typename std::enable_if<
sizeof ...(Is) != sample_size(Size,Stride,Off),
std::array<T, sample_size(Size,Stride,Off)>
>::type
get_sample(std::array<T,Size> const & data)
{
return
get_sample<Stride,Off,T,Size,Is...,(sizeof...(Is) * Stride) + Off>
(data);
}
By default Stride is 1 and Off is 0. The helper function sample_size
embeds the convention that if Stride is 0 you get an empty sample.
For an illustrative program, you can append:
constexpr std::array<int,5> data1{{0,1,2,3,4}};
constexpr auto sample1 = get_sample(data1);
constexpr auto sample2 = get_sample<2>(data1);
constexpr auto sample3 = get_sample<2,1>(data1);
constexpr auto sample4 = get_sample<6>(data1);
constexpr auto sample5 = get_sample<6,5>(data1);
static_assert(sample5.size() == 0,"");
constexpr std::array<float,6> data2{{1.1,2.2,3.3,4.4,5.5,6.6}};
constexpr auto sample6 = get_sample<2>(data2);
constexpr auto sample7 = get_sample<2,3>(data2);
constexpr auto sample8 = get_sample<3,2>(data2);
constexpr std::array<int,0> data3{};
constexpr auto sample9 = get_sample<0>(data3);
static_assert(sample9.size() == 0,"");
constexpr auto sample10 = get_sample<2>(data3);
static_assert(sample10.size() == 0,"");
#include <iostream>
int main()
{
std::cout << "get_sample<> of {0,1,2,3,4}\n";
for (auto const & e : sample1) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<2> of {0,1,2,3,4}\n";
for (auto const & e : sample2) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<2,1> of {0,1,2,3,4}\n";
for (auto const & e : sample3) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<6> of {0,1,2,3,4}\n";
for (auto const & e : sample4) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<2> of {{1.1,2.2,3.3,4.4,5.5,6.6}}\n";
for (auto const & e : sample6) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<2,3> of {{1.1,2.2,3.3,4.4,5.5,6.6}}\n";
for (auto const & e : sample7) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<3,2> of {{1.1,2.2,3.3,4.4,5.5,6.6}}\n";
for (auto const & e : sample8) {
std::cout << e << ' ';
}
std::cout << '\n';
return 0;
}
which reports:
get_sample<> of {0,1,2,3,4}
0 1 2 3 4
get_sample<2> of {0,1,2,3,4}
0 2 4
get_sample<2,1> of {0,1,2,3,4}
1 3
get_sample<6> of {0,1,2,3,4}
0
get_sample<2> of {1.1,2.2,3.3,4.4,5.5,6.6}
1.1 3.3 5.5
get_sample<2,3> of {1.1,2.2,3.3,4.4,5.5,6.6}
4.4 6.6
get_sample<3,2> of {1.1,2.2,3.3,4.4,5.5,6.6}
3.3 6.6
See it live
If you want to apply this to your class Def, you would redefine it in an amenable
way like:
struct Def {
static constexpr int N = 5;
static constexpr std::array<double,N> data{{0,1,2,3,4}};
};
and get your compiletime sample like:
constexpr auto s = get_sample<2,1>(Def::data);
(g++ 6.1/clang++ 3.8, -std=c++11 -Wall -Wextra -pedantic)
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.
Is there an easy way to get a slice of an array in C++?
I.e., I've got
array<double, 10> arr10;
and want to get array consisting of five first elements of arr10:
array<double, 5> arr5 = arr10.???
(other than populating it by iterating through first array)
The constructors for std::array are implicitly defined so you can't initialize it with a another container or a range from iterators. The closest you can get is to create a helper function that takes care of the copying during construction. This allows for single phase initialization which is what I believe you're trying to achieve.
template<class X, class Y>
X CopyArray(const Y& src, const size_t size)
{
X dst;
std::copy(src.begin(), src.begin() + size, dst.begin());
return dst;
}
std::array<int, 5> arr5 = CopyArray<decltype(arr5)>(arr10, 5);
You can also use something like std::copy or iterate through the copy yourself.
std::copy(arr10.begin(), arr10.begin() + 5, arr5.begin());
Sure. Wrote this:
template<int...> struct seq {};
template<typename seq> struct seq_len;
template<int s0,int...s>
struct seq_len<seq<s0,s...>>:
std::integral_constant<std::size_t,seq_len<seq<s...>>::value> {};
template<>
struct seq_len<seq<>>:std::integral_constant<std::size_t,0> {};
template<int Min, int Max, int... s>
struct make_seq: make_seq<Min, Max-1, Max-1, s...> {};
template<int Min, int... s>
struct make_seq<Min, Min, s...> {
typedef seq<s...> type;
};
template<int Max, int Min=0>
using MakeSeq = typename make_seq<Min,Max>::type;
template<std::size_t src, typename T, int... indexes>
std::array<T, sizeof...(indexes)> get_elements( seq<indexes...>, std::array<T, src > const& inp ) {
return { inp[indexes]... };
}
template<int len, typename T, std::size_t src>
auto first_elements( std::array<T, src > const& inp )
-> decltype( get_elements( MakeSeq<len>{}, inp ) )
{
return get_elements( MakeSeq<len>{}, inp );
}
Where the compile time indexes... does the remapping, and MakeSeq makes a seq from 0 to n-1.
Live example.
This supports both an arbitrary set of indexes (via get_elements) and the first n (via first_elements).
Use:
std::array< int, 10 > arr = {0,1,2,3,4,5,6,7,8,9};
std::array< int, 6 > slice = get_elements(arr, seq<2,0,7,3,1,0>() );
std::array< int, 5 > start = first_elements<5>(arr);
which avoids all loops, either explicit or implicit.
2018 update, if all you need is first_elements:
Less boilerplaty solution using C++14 (building up on Yakk's pre-14 answer and stealing from "unpacking" a tuple to call a matching function pointer)
template < std::size_t src, typename T, int... I >
std::array< T, sizeof...(I) > get_elements(std::index_sequence< I... >, std::array< T, src > const& inp)
{
return { inp[I]... };
}
template < int N, typename T, std::size_t src >
auto first_elements(std::array<T, src > const& inp)
-> decltype(get_elements(std::make_index_sequence<N>{}, inp))
{
return get_elements(std::make_index_sequence<N>{}, inp);
}
Still cannot explain why this works, but it does (for me on Visual Studio 2017).
This answer might be late... but I was just toying around with slices - so here is my little home brew of std::array slices.
Of course, this comes with a few restrictions and is not ultimately general:
The source array from which a slice is taken must not go out of scope. We store a reference to the source.
I was looking for constant array slices first and did not try to expand this code to both const and non const slices.
But one nice feature of the code below is, that you can take slices of slices...
// ParCompDevConsole.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "pch.h"
#include <cstdint>
#include <iostream>
#include <array>
#include <stdexcept>
#include <sstream>
#include <functional>
template <class A>
class ArraySliceC
{
public:
using Array_t = A;
using value_type = typename A::value_type;
using const_iterator = typename A::const_iterator;
ArraySliceC(const Array_t & source, size_t ifirst, size_t length)
: m_ifirst{ ifirst }
, m_length{ length }
, m_source{ source }
{
if (source.size() < (ifirst + length))
{
std::ostringstream os;
os << "ArraySliceC::ArraySliceC(<source>,"
<< ifirst << "," << length
<< "): out of bounds. (ifirst + length >= <source>.size())";
throw std::invalid_argument( os.str() );
}
}
size_t size() const
{
return m_length;
}
const value_type& at( size_t index ) const
{
return m_source.at( m_ifirst + index );
}
const value_type& operator[]( size_t index ) const
{
return m_source[m_ifirst + index];
}
const_iterator cbegin() const
{
return m_source.cbegin() + m_ifirst;
}
const_iterator cend() const
{
return m_source.cbegin() + m_ifirst + m_length;
}
private:
size_t m_ifirst;
size_t m_length;
const Array_t& m_source;
};
template <class T, size_t SZ>
std::ostream& operator<<( std::ostream& os, const std::array<T,SZ>& arr )
{
if (arr.size() == 0)
{
os << "[||]";
}
else
{
os << "[| " << arr.at( 0 );
for (auto it = arr.cbegin() + 1; it != arr.cend(); it++)
{
os << "," << (*it);
}
os << " |]";
}
return os;
}
template<class A>
std::ostream& operator<<( std::ostream& os, const ArraySliceC<A> & slice )
{
if (slice.size() == 0)
{
os << "^[||]";
}
else
{
os << "^[| " << slice.at( 0 );
for (auto it = slice.cbegin() + 1; it != slice.cend(); it++)
{
os << "," << (*it);
}
os << " |]";
}
return os;
}
template<class A>
A unfoldArray( std::function< typename A::value_type( size_t )> producer )
{
A result;
for (size_t i = 0; i < result.size(); i++)
{
result[i] = producer( i );
}
return result;
}
int main()
{
using A = std::array<float, 10>;
auto idf = []( size_t i ) -> float { return static_cast<float>(i); };
const auto values = unfoldArray<A>(idf);
std::cout << "values = " << values << std::endl;
// zero copy slice of values array.
auto sl0 = ArraySliceC( values, 2, 4 );
std::cout << "sl0 = " << sl0 << std::endl;
// zero copy slice of the sl0 (the slice of values array)
auto sl01 = ArraySliceC( sl0, 1, 2 );
std::cout << "sl01 = " << sl01 << std::endl;
return 0;
}