I am attempting to implement a packed_bits class using variadic templates and std::bitset.
In particular, I am running into problems writing a get function which returns a reference to a subset of the member m_bits which contains all the packed bits. The function should be analogous to std::get for std::tuple.
It should act as an reference overlay so I can manipulate a subset of packed_bits.
For instance,
using my_bits = packed_bits<8,16,4>;
my_bits b;
std::bitset< 8 >& s0 = get<0>( b );
std::bitset< 16 >& s1 = get<1>( b );
std::bitset< 4 >& s2 = get<2>( b );
UPDATE
Below is the code that has been rewritten according to Yakk's recommendations below. I am stuck at the point of his last paragraph: not sure how to glue together the individual references into one bitset-like reference. Thinking/working on that last part now.
UPDATE 2
Okay, my new approach is going to be to let bit_slice<> do the bulk of the work:
it is meant to be short-lived
it will publicly subclass std::bitset<length>, acting as a temporary buffer
on construction, it will copy from packed_bits<>& m_parent;
on destruction, it will write to m_parent
in addition to the reference via m_parent, it must also know offset, length
get<> will become a free-function which takes a packet_bits<> and returns a bit_slice<> by value instead of bitset<> by reference
There are various short-comings to this approach:
bit_slice<> has to be relatively short-lived to avoid aliasing issues, since we only update on construction and destruction
we must avoid multiple overlapping references while coding (whether threaded or not)
we will be prone to slicing if we attempt to hold a pointer to the base class when we have an instance of the child class
but I think this will be sufficient for my needs. I will post the finished code when it is complete.
UPDATE 3
After fighting with the compiler, I think I have a basic version working. Unfortunately, I could not get the free-floating ::get() to compile properly: BROKEN shows the spot. Otherwise, I think it's working.
Many thanks to Yakk for his suggestions: the code below is about 90%+ based on his comments.
UPDATE 4
Free-floating ::get() fixed.
UPDATE 5
As suggested by Yakk, I have eliminated the copy. bit_slice<> will read on get_value() and write on set_value(). Probably 90%+ of my calls will be through these interfaces anyways, so no need to subclass/copy.
No more dirtiness.
CODE
#include <cassert>
#include <bitset>
#include <iostream>
// ----------------------------------------------------------------------------
template<unsigned... Args>
struct Add { enum { val = 0 }; };
template<unsigned I,unsigned... Args>
struct Add<I,Args...> { enum { val = I + Add<Args...>::val }; };
template<int IDX,unsigned... Args>
struct Offset { enum { val = 0 }; };
template<int IDX,unsigned I,unsigned... Args>
struct Offset<IDX,I,Args...> {
enum {
val = IDX>0 ? I + Offset<IDX-1,Args...>::val : Offset<IDX-1,Args...>::val
};
};
template<int IDX,unsigned... Args>
struct Length { enum { val = 0 }; };
template<int IDX,unsigned I,unsigned... Args>
struct Length<IDX,I,Args...> {
enum {
val = IDX==0 ? I : Length<IDX-1,Args...>::val
};
};
// ----------------------------------------------------------------------------
template<unsigned... N_Bits>
struct seq
{
static const unsigned total_bits = Add<N_Bits...>::val;
static const unsigned size = sizeof...( N_Bits );
template<int IDX>
struct offset
{
enum { val = Offset<IDX,N_Bits...>::val };
};
template<int IDX>
struct length
{
enum { val = Length<IDX,N_Bits...>::val };
};
};
// ----------------------------------------------------------------------------
template<unsigned offset,unsigned length,typename PACKED_BITS>
struct bit_slice
{
PACKED_BITS& m_parent;
bit_slice( PACKED_BITS& t ) :
m_parent( t )
{
}
~bit_slice()
{
}
bit_slice( bit_slice const& rhs ) :
m_parent( rhs.m_parent )
{ }
bit_slice& operator=( bit_slice const& rhs ) = delete;
template<typename U_TYPE>
void set_value( U_TYPE u )
{
for ( unsigned i=0; i<length; ++i )
{
m_parent[offset+i] = u&1;
u >>= 1;
}
}
template<typename U_TYPE>
U_TYPE get_value() const
{
U_TYPE x = 0;
for ( int i=length-1; i>=0; --i )
{
if ( m_parent[offset+i] )
++x;
if ( i!=0 )
x <<= 1;
}
return x;
}
};
template<typename SEQ>
struct packed_bits :
public std::bitset< SEQ::total_bits >
{
using bs_type = std::bitset< SEQ::total_bits >;
using reference = typename bs_type::reference;
template<int IDX> using offset = typename SEQ::template offset<IDX>;
template<int IDX> using length = typename SEQ::template length<IDX>;
template<int IDX> using slice_type =
bit_slice<offset<IDX>::val,length<IDX>::val,packed_bits>;
template<int IDX>
slice_type<IDX> get()
{
return slice_type<IDX>( *this );
}
};
template<int IDX,typename T>
typename T::template slice_type<IDX>
get( T& t )
{
return t.get<IDX>();
};
// ----------------------------------------------------------------------------
int main( int argc, char* argv[] )
{
using my_seq = seq<8,16,4,8,4>;
using my_bits = packed_bits<my_seq>;
using my_slice = bit_slice<8,16,my_bits>;
using slice_1 =
bit_slice<my_bits::offset<1>::val,my_bits::length<1>::val,my_bits>;
my_bits b;
my_slice s( b );
slice_1 s1( b );
assert( sizeof( b )==8 );
assert( my_seq::total_bits==40 );
assert( my_seq::size==5 );
assert( my_seq::offset<0>::val==0 );
assert( my_seq::offset<1>::val==8 );
assert( my_seq::offset<2>::val==24 );
assert( my_seq::offset<3>::val==28 );
assert( my_seq::offset<4>::val==36 );
assert( my_seq::length<0>::val==8 );
assert( my_seq::length<1>::val==16 );
assert( my_seq::length<2>::val==4 );
assert( my_seq::length<3>::val==8 );
assert( my_seq::length<4>::val==4 );
{
auto s2 = b.get<2>();
}
{
auto s2 = ::get<2>( b );
s2.set_value( 25 ); // 25==11001, but only 4 bits, so we take 1001
assert( s2.get_value<unsigned>()==9 );
}
return 0;
}
I wouldn't have get return a bitset, because each bitset needs to manage its own memory.
Instead, I'd use a bitset internally to manage all of the bits, and create bitset::reference-like individual bit references, and bitset-like "slices", which get can return.
A bitslice would have a pointer back to the original packed_bits, and would know the offset where it starts, and how wide it is. It's references to individual bits would be references from the original packed_bits, which are references from the internal bitset, possibly.
Your Size is redundant -- sizeof...(pack) tells you how many elements are in the pack.
I'd pack up the sizes of the slices into a seqence so you can pass it around easier. Ie:
template<unsigned... Vs>
struct seq {};
is a type from which you can extract an arbitrary length list of unsigned ints, yet can be passed as a single parameter to a template.
As a first step, write bit_slice<offset, length>, which takes a std::bitset<size> and produces bitset::references to individual bits, where bit_slice<offset, length>[n] is the same as bitset[n+offset].
Optionally, bit_slice could store offset as a run-time parameter (because offset as a compile-time parameter is just an optimization, and not that strong of one I suspect).
Once you have bit_slice, working on the tuple syntax of packed_bits is feasible. get<n, offset=0>( packed_bits<a,b,c,...>& ) returns a bit_slice<x> determined by indexing the packed_bits sizes, with an offset determined by adding the first n-1 packed_bits sizes, which is then constructed from the internal bitset of the packed_bits.
Make sense?
Apparently not. Here is a quick bit_slice that represents some sub-range of bits within a std::bitset.
#include <bitset>
template<unsigned Width, unsigned Offset, std::size_t SrcBitWidth>
struct bit_slice {
private:
std::bitset<SrcBitWidth>* bits;
public:
// cast to `bitset`:
operator std::bitset<Width>() const {
std::bitset<Width> retval;
for(unsigned i = 0; i < Offset; ++i) {
retval[i] = (*this)[i];
}
return retval;
}
typedef typename std::bitset<SrcBitWidth>::reference reference;
reference operator[]( size_t pos ) {
// TODO: check that pos < Width?
return (*bits)[pos+Offset];
}
constexpr bool operator[]( size_t pos ) const {
// TODO: check that pos < Width?
return (*bits)[pos+Offset];
}
typedef bit_slice<Width, Offset, SrcBitWidth> self_type;
// can be assigned to from any bit_slice with the same width:
template<unsigned O_Offset, unsigned O_SrcBitWidth>
self_type& operator=( bit_slice<Width, O_Offset, O_SrcBitWidth>&& o ) {
for (unsigned i = 0; i < Width; ++i ) {
(*this)[i] = o[i];
}
return *this;
}
// can be assigned from a `std::bitset<Width>` of the same size:
self_type& operator=( std::bitset<Width> const& o ) {
for (unsigned i = 0; i < Width; ++i ) {
(*this)[i] = o[i];
}
return *this;
}
explicit bit_slice( std::bitset<SrcBitWidth>& src ):bits(&src) {}
bit_slice( self_type const& ) = default;
bit_slice( self_type&& ) = default;
bit_slice( self_type&o ):bit_slice( const_cast<self_type const&>(o)) {}
// I suspect, but am not certain, the the default move/copy ctor would do...
// dtor not needed, as there is nothing to destroy
// TODO: reimplement rest of std::bitset's interface that you care about
};
template<unsigned offset, unsigned width, std::size_t src_width>
bit_slice< width, offset, src_width > make_slice( std::bitset<src_width>& src ) {
return bit_slice< width, offset, src_width >(src);
}
#include <iostream>
int main() {
std::bitset<16> bits;
bits[8] = true;
auto slice = make_slice< 8, 8 >( bits );
bool b0 = slice[0];
bool b1 = slice[1];
std::cout << b0 << b1 << "\n"; // should output 10
}
Another useful class would be a bit_slice with runtime offset-and-source size. This will be less efficient, but easier to program against.
I'm going to guess it something like this:
#include <iostream>
#include <bitset>
using namespace std;
template<int N, int L, int R>
bitset<L-R>
slice(bitset<N> value)
{
size_t W = L - R + 1;
if (W > sizeof(uint64_t) * 8) {
W = 31;
throw("Exceeding integer word size");
}
uint64_t svalue = (value.to_ulong() >> R) & ((1 << W) - 1);
return bitset<L-R>{svalue};
}
}
int main()
{
bitset<16> beef { 0xBEEF };
bitset<4-3> sliced_beef = slice<16, 4, 3>(beef);
auto fast_sliced_beef = slice<16, 4, 3>(beef);
return 0;
}
Related
You may be familiar with Python decorators such as #lru_cache. It wraps any function and does MEMOIZATION of results improving the runtime:
#functools.lru_cache(maxsize=100)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
I want to build #lru_cache decorator in C++.
My implementation have one problem - when I wrap a recursive function and call it in a wrapping class, all subsequent recursive calls have no access to the cache. And as a result, I have 0 hits. Let me illustrate (I'll skip a bit of code).
LRUCache class:
template <typename Key, typename Val>
class LRUCache
{
public:
LRUCache( int capacity = 100 ) : capacity{ capacity } {}
Val get( Key key ) {... }
void put( Key key, Val value ) {... }
...
private:
int capacity;
std::list<std::pair<Val, Key>> CACHE;
std::unordered_map<Key, typename std::list<std::pair<Val, Key>>::iterator> LOOKUP;
};
_LruCacheFunctionWrapper class:
template <typename Key, typename Val>
class _LruCacheFunctionWrapper
{
struct CacheInfo {... };
public:
_LruCacheFunctionWrapper( std::function<Val( Key )> func, int maxSize )
: _wrapped{ func }
, _cache{ maxSize }
, _hits{ 0 }
, _misses{ 0 }
, _maxsize{ maxSize }
{}
template<typename... Args>
Val operator()( Args... args )
{
auto res = _cache.get( args... );
if( res == -1 )
{
++_misses;
res = _wrapped( args... );
_cache.put( args..., res );
}
else
++_hits;
return res;
}
CacheInfo getCacheInfo() {... }
void clearCache() {... }
private:
std::function<Val( Key )> _wrapped;
LRUCache<Key, Val> _cache;
int _hits;
int _misses;
int _maxsize;
};
And lastly, the target function:
long long fib( int n )
{
if( n < 2 )
return n;
return fib( n - 1 ) + fib( n - 2 );
}
You may see that the line:
res = _wrapped( args... );
is sending me into the function scope and I have to recalculate all recursive calls. How can I solve it?
Main.cpp:
_LruCacheFunctionWrapper<int, long long> wrapper( &fib, 50 );
for( auto i = 0; i < 16; ++i )
std::cout << wrapper( i ) << " ";
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 )
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;
}
I have a large lookup table that currently needs 12 bits per entry. Is there a standard class that will give me a memory efficient container for storing odd-sized data? I have about a billion items in the table, so I care more about memory efficiency than speed.
I need to be able to get the underlying data and read/write it to a file as well.
How about this:
#include <stdio.h>
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned int uint;
typedef unsigned long long int qword;
enum {
bits_per_cell = 12, cellmask = (1<<bits_per_cell)-1,
N_cells = 1000000,
bufsize = (N_cells*bits_per_cell+7)/8,
};
byte* buf;
byte* Alloc( void ) {
buf = new byte[bufsize];
return buf;
};
// little-endian only
void put( uint i, uint c ) {
qword x = qword(i)*bits_per_cell;
uint y = x&15, z = (x>>4)<<1;
uint& a = (uint&)buf[z];
uint mask = ~(cellmask<<y);
a = a & mask | ((c&cellmask)<<y);
}
uint get( uint i ) {
qword x = qword(i)*bits_per_cell;
uint y = x&15, z = (x>>4)<<1;
uint& a = (uint&)buf[z];
return (a>>y)&cellmask;
}
/*
// bigendian/universal
void put( uint i, uint c ) {
qword x = qword(i)*bits_per_cell;
uint y = x&7, z = (x>>3);
uint a = buf[z] + (buf[z+1]<<8) + (buf[z+2]<<16);
uint mask = ~(cellmask<<y);
a = a & mask | ((c&cellmask)<<y);
buf[z] = byte(a); buf[z+1]=byte(a>>8); buf[z+2]=byte(a>>16);
}
uint get( uint i ) {
qword x = qword(i)*bits_per_cell;
uint y = x&7, z = (x>>3);
uint a = buf[z] + (buf[z+1]<<8) + (buf[z+2]<<16);
return (a>>y)&cellmask;
}
*/
int main( void ) {
if( Alloc()==0 ) return 1;
uint i;
for( i=0; i<N_cells; i++ ) put( i^1, i );
for( i=0; i<N_cells; i++ ) {
uint j = i^1, c, d;
c = get(j); d = i & cellmask;
if( c!=d ) printf( "x[%08X]=%04X, not %04X\n", j,c,d );
}
}
Have you looked at Boost::dynamic_bitset? I'm not saying that it would be the be-all, end all of your dreams but it could help you with some of the characteristics you've described. It's very similar to bitset of the standard library, only with resizeable options.
I might not try to use it by itself to solve your problem. Instead, I might combine it with another container class and use it in conjunction with some sort of mapping scheme. I don't know what type of mapping as it will depend on the data and the frequency of cycles. However, thinking more about this:
std::vector<std::bitset<12> > oneBillionDollars; //Austin Powers, my hero!
You have a problem of packing. The only idea I can get is that you'd want to find a LCM of N and some power of two. Not gonna be so easy, but definitely workable.
Also, you cannot really manipulate some weird sized data, so you need to pack it into a bigger integer type. The table will contain the data packed, but the "accessor" will yield an unpacked one.
// General structure
template <size_t N>
class Pack
{
public:
static size_t const Number = N;
static size_t const Density = 0; // number of sets of N bits
typedef char UnpackedType; // some integral
UnpackedType Get(size_t i) const; // i in [0..Density)
void Set(size_t i, UnpackedType t); // i in [0..Density)
// arbitrary representation
};
// Example, for 12 bits
// Note: I assume that all is set, you'll have to pad...
// but for a million one or two more should not be too much of an issue I guess
// if it is, the table shall need one more data member, which is reasonnable
class Pack12
{
public:
typedef uint16_t UnpackedType;
static size_t const Number = 12;
static size_t const Density = 4;
UnpackedType get(size_t i) const;
void set(size_t i, UnpackedType t);
private:
uint16_t data[3];
};
Now we can build on that to build a generic table that'll work for any pack:
template <typename Pack>
class Table
{
public:
typedef typename Pack::UnpackedType UnpackedType;
bool empty() const;
size_t size() const;
UnpackedType get(size_t i) const;
void set(size_t i, UnpackedType t);
private:
static size_t const NumberBits = Pack::Number;
static size_t const Density = Pack::Density;
std::deque<Pack> data;
};
template <typename Pack>
bool Table<Pack>::empty() const { return data.empty(); }
template <typename Pack>
size_t Table<Pack>::size() const { return data.size() * Density; }
template <typename Pack>
typename Table<Pack>::UnpackedType Table<Pack>::get(size_t i) const
{
Pack const& pack = data.at(i / Density);
return pack.get(i % Density);
}
// Table<Pack>::set is the same
A more clever way would be for Pack<N> to be able to deduce the getters and representations... but it doesn't seem worth the effort because the Pack interface is minimal and Table can present a richer interface without asking more than that.