I call a function like this:
void do_work(void* ptr, size_t size)
{
//do things with ptr and size
}
The question is: is it possible to call the function like do_work(one); and somehow get the sizeof inside the function?
Given one is a static array (int one[10];) or non array (int one;), is it possible now?
In C++ you could use template argumment deduction to get the type of the pointer (the pointed type) and then compute its sizeof:
template<typename T>
void do_work( T* ptr )
{
constexpr std::size_t size = sizeof(T);
}
You can use a function template:
template <typename T>
void do_work(T* stuff)
{
const size_t s = sizeof(T);
....
}
Edit: version for arrays:
template<typename T, size_t N >
void do_work(T (&array)[N] )
{
const size_t s = N * sizeof(T);
}
No, not for void *, you will not be able to compute the size of the object whose address is passed.
template <typename T>
void do_work(T * t) { do_work(t, sizeof(T)); }
template<typename T>
void do_work(T* ptr)
{
size_t size = sizeof(T);
size = sizeof(*ptr); // or this
}
I don't know C++, this question appeared before me, probably because it previously had the C tag. But here's something that you could do in C, probably also in C++:
#define do_work(_a_) do_work_func(_a_, sizeof(_a_))
void do_work_func( void* ptr, size_t size )
{
//do things with ptr and size
}
Which would work out, given that one in do_work( one ); call had been defined as an array.
If I get you right you might want:
#include <iostream>
void do_work(void* ptr, size_t size)
{
std::cout << "Size: " << size << '\n';
}
template <typename T>
void do_work(T& single) {
do_work(&single, 1);
}
template <typename T, std::size_t N>
void do_work(T (&array)[N]) {
do_work(array, N);
}
template <typename T>
void do_work(T* multiple, std::size_t n) {
do_work(static_cast<void*>(multiple), n);
}
int main()
{
int a = 1;
int b[] = { 1, 2, 3 };
int* c = b;
do_work(a);
do_work(b);
do_work(c, 3);
}
Note: Why the void* ?
Related
I'm trying to make a simple template of creating an array class. However, I want to use two arguments: one typename and one int variable. I'm running into two errors and I'm not sure what I'm doing wrong. Here is my source which is simple and works.
int main()
{
Array<int, 7> tArray;
std::cout << tArray.getSize() << std::endl;
for (int i = 0; i < tArray.getSize(); i++)
{
tArray.insert(i + 5);
i++;
}
tArray.printArray();
return 0;
}
The error/issue I'm having is regarding two functions in my header file.
#pragma once
template <typename T, int S>
class Array
{
private:
T arr[S];
public:
Array();
int getSize() const;
int insert(T val);
void printArray();
~Array();
};
cArray<T, S>::cArray() { }
template <typename T, int S>
int Array<T, S>::insert(T val)
{
Array<T, S>* temp = new Array[S];
temp = this;
return temp[val];
}
template <typename T, int S>
void Array<T, S>::printArray()
{
Array<T, S>* temp = new Array[S];
temp = this;
for (int i = 0; i < S; i++)
{
std::cout << tempArray[i] << std::endl;
}
}
The functions insert and printArray are not working. For the insert function, the error being returned states: Suppression State Error C2440 'return': cannot convert from 'Array<int,7>' to 'int'
The printArray is displaying nothing.
This is the first time I'm using a template with two arguments so I'm a bit confused on what I'm doing wrong here. Any takes?
The offending code is here:
template <typename T, int S>
int Array<T, S>::insert(T val) {
Array<T, S>* temp = new Array[S];
temp = this;
return temp[val];
}
First, you create an array of Arrays, because you wrote an expression that looks like:
Type *var = new Type[count];
Then you change the pointer to that array to this. Then you try to return the element at index val of the array temp, and this element has type Array<T, S>. But, the return type is int, so, that is why the compiler complains that it cannot convert Array<int, 7> to int.
You should not use new here at all. You already have allocated memory for the array (it's the member variable arr), you just need to copy val into the right place in that array. To do that, you need to keep track of how many elements you already inserted in the array. (Note that S is a constant that just says what the maximum amount of elements is you can store.) So:
template <typename T, int S>
class Array {
T arr[S];
size_t size = 0;
...
};
template <typename T, int S>
void Array<T, S>::insert(T val)
{
arr[size++] = val;
}
template <typename T, int S>
size_t Array<T, S>::getSize()
{
return size;
}
And printArray() has similar issues. You should print the contents of arr, you should not create a new temporary array and try to print its elements.
For a school assignment I want to build a custom array container as I'm not allowed to use containers provided by std, or any library available, while self made is allowed.
So far everything I have is working but I want to double my array size as soon as I reach the limit. how can i do this, all i can find is using vector (which i'm not allowed).
#ifndef UTILS_ARRAY_HEADER_INCLUDED
#define UTILS_ARRAY_HEADER_INCLUDED
#include <array>
namespace utils
{
template <class T>
struct Array
{
private:
int count = 0;
int size = 1;
std::array<T, 1> myArray;
void doubleSize();
public:
T* begin();
T* end();
T& operator[] (int);
void addItem(T const);
};
template <class T>
T* Array<T>::begin()
{
return &myArray[0];
}
template <class T>
T* Array<T>::end()
{
if (&myArray[count])
return &myArray[count];
return &myArray[0];
}
template <class T>
T& Array<T>::operator[] (int key)
{
return myArray[key];
}
template <class T>
void Array<T>::addItem(T const item)
{
if (count >= 0 && count < size)
{
myArray[count] = item;
count++;
}
else {
doubleSize();
}
return;
}
template <class T>
void Array<T>::doubleSize()
{
// ?
/*size = size * 2;
const int newsize = 2;
std::array<T, newsize> newArray; // not working.
std::copy(std::begin(myArray), std::end(myArray), std::begin(newArray));
myArray = newArray;*/
}
}
#endif
You need properties:
current capacity: int
current size (max used index): int
pointer to your data: T *
In AddItem check if current_size < current_capacity. If yes,
create new_data with size of currernt_capacity * 2, copy each item, delete old data and replace pointer.
Remember to do delete data; in destructor. I won't give you more code, it's your homework.
Checkout valgrind to check if your code does not leak memory.
I suspect that std::array<T, 1> is not allowed either. But that's not a real problem: you can't use array anyway since it has a fixed size.
You'll need to store a T* begin, a size_t current_size and a size_t size_in_use.
I am thinking about following problem:
Let us have a merging function for merge arrays defined in following way:
// input is (const void*, size_t, const void*, size_t,...)
template<typename...ARGS>
MyArray Concatenation(ARGS...args)
And let us have couple of structs with static properties
struct A { static void* DATA; static size_t SIZE; };
struct B { static void* DATA; static size_t SIZE; };
struct C { static void* DATA; static size_t SIZE; };
I would like to have a method:
template<typename...ARGS>
MyArray AutoConcatenation();
Where ARGS should be structs with mentioned static interface.
Following methods should have the same output:
AutoConcatenation<A, B, C>();
Concatenation(A::DATA, A::SIZE, B::DATA, B::SIZE, C::DATA, C::SIZE);
My question is how to implement parameter pack expansion.
I tried:
// not working
template<typename...ARGS>
MyArray AutoConcatenation()
{
return Concatenation((ARGS::DATA, ARGS::SIZE)...);
}
What about expansions
ARGS::DATA... // Correct expansion of pointers
ARGS::SIZE... // Correct expansion of sizes
(ARGS::DATA, ARGS::SIZE)... // Seems to be expansion of sizes
Just info for advisors. I am looking for implementation of AutoConcatenation method, not for its redeclaration nor for redeclaration previous code, thank you.
A lazy solution using std::tuple:
make a tuple of DATA and SIZE for each element of the parameter pack,
flatten the list of tuples to one big tuple using std::tuple_cat,
apply the resulting tuple's elements to Concatenation by expanding a list of indexes in an std::index_sequence.
In the following code, the test harness is longer than the actual solution:
#include <cstddef>
#include <tuple>
#include <utility>
#include <iostream>
#include <typeinfo>
#include <type_traits>
struct MyArray { };
template<class... ARGS> MyArray Concatenation(ARGS... args)
{
// Just some dummy code for testing.
using arr = int[];
(void)arr{(std::cout << typeid(args).name() << ' ' << args << '\n' , 0)...};
return {};
}
struct A { static void* DATA; static std::size_t SIZE; };
struct B { static void* DATA; static std::size_t SIZE; };
struct C { static void* DATA; static std::size_t SIZE; };
// Also needed for testing.
void* A::DATA;
std::size_t A::SIZE;
void* B::DATA;
std::size_t B::SIZE;
void* C::DATA;
std::size_t C::SIZE;
// The useful stuff starts here.
template<class T, std::size_t... Is> MyArray concat_hlp_2(const T& tup, std::index_sequence<Is...>)
{
return Concatenation(std::get<Is>(tup)...);
}
template<class T> MyArray concat_hlp_1(const T& tup)
{
return concat_hlp_2(tup, std::make_index_sequence<std::tuple_size<T>::value>{});
}
template<class... ARGS> MyArray AutoConcatenation()
{
return concat_hlp_1(std::tuple_cat(std::make_tuple(ARGS::DATA, ARGS::SIZE)...));
}
int main()
{
AutoConcatenation<A, B, C>();
}
If you want to avoid std::tuple and std::tuple_cat (which can be heavy in terms of compile times), here's an alternative using indexes into arrays.
The testing code stays the same, this is just the juicy stuff:
template<std::size_t Size> const void* select(std::false_type, std::size_t idx,
const void* (& arr_data)[Size], std::size_t (&)[Size])
{
return arr_data[idx];
}
template<std::size_t Size> std::size_t select(std::true_type, std::size_t idx,
const void* (&)[Size], std::size_t (& arr_size)[Size])
{
return arr_size[idx];
}
template<std::size_t... Is> MyArray concat_hlp(std::index_sequence<Is...>,
const void* (&& arr_data)[sizeof...(Is) / 2], std::size_t (&& arr_size)[sizeof...(Is) / 2])
{
return Concatenation(select(std::bool_constant<Is % 2>{}, Is / 2, arr_data, arr_size)...);
}
template<class... ARGS> MyArray AutoConcatenation()
{
return concat_hlp(std::make_index_sequence<sizeof...(ARGS) * 2>{}, {ARGS::DATA...}, {ARGS::SIZE...});
}
Again a sequence of indexes twice the size of the original parameter pack, but we build separate arrays of DATA and SIZE and then use tag dispatching to select elements from one or the other depending on the parity of the current index.
This may not look as nice as the previous code, but it doesn't involve any template recursion (std::make_index_sequence is implemented using compiler intrinsics in modern compilers as far as I know) and cuts down on the number of template instantiations, so it should be faster to compile.
The select helper can be made constexpr by using arrays of pointers to the static members, but this turns out to be unnecessary in practice. I've tested MSVC 2015 U2, Clang 3.8.0 and GCC 6.1.0 and they all optimize this to a direct call to Concatenation (just like for the tuple-based solution).
I think the following is more elegant, and it illustrates the common recursive unpacking pattern. Finally, it does not perform any voodoo with memory layouts and tries to be idiomatic as far as C++ generic programming.
#include <iostream>
#include <string>
using namespace std;
// Handle zero arguments.
template <typename T = string>
T concat_helper() { return T(); }
// Handle one pair.
template <typename T = string>
T concat_helper(const T &first, size_t flen) { return first; }
// Handle two or more pairs. Demonstrates the recursive unpacking pattern
// (very common with variadic arguments).
template <typename T = string, typename ...ARGS>
T concat_helper(const T &first, size_t flen,
const T &second, size_t slen,
ARGS ... rest) {
// Your concatenation code goes here. We're assuming we're
// working with std::string, or anything that has method length() and
// substr(), with obvious behavior, and supports the + operator.
T concatenated = first.substr(0, flen) + second.substr(0, slen);
return concat_helper<T>(concatenated, concatenated.length(), rest...);
}
template <typename T, typename ...ARGS>
T Concatenate(ARGS...args) { return concat_helper<T>(args...); }
template <typename T>
struct pack {
T data;
size_t dlen;
};
template <typename T>
T AutoConcatenate_helper() { return T(); }
template <typename T>
T AutoConcatenate_helper(const pack<T> *packet) {
return packet->data;
}
template <typename T, typename ...ARGS>
T AutoConcatenate_helper(const pack<T> *first, const pack<T> *second,
ARGS...rest) {
T concatenated = Concatenate<T>(first->data, first->dlen,
second->data, second->dlen);
pack<T> newPack;
newPack.data = concatenated;
newPack.dlen = concatenated.length();
return AutoConcatenate_helper<T>(&newPack, rest...);
}
template <typename T, typename ...ARGS>
T AutoConcatenate(ARGS...args) {
return AutoConcatenate_helper<T>(args...);
}
int main() {
pack<string> first;
pack<string> second;
pack<string> third;
pack<string> last;
first.data = "Hello";
first.dlen = first.data.length();
second.data = ", ";
second.dlen = second.data.length();
third.data = "World";
third.dlen = third.data.length();
last.data = "!";
last.dlen = last.data.length();
cout << AutoConcatenate<string>(&first, &second, &third, &last) << endl;
return 0;
}
We're neither changing the declaration of Concatenate<>(), nor that of AutoConcatenate<>(), as required. We're free to implement AutoConcatenate<>(), as we did, and we assume that there is some implementation of Concatenate<>() (we provided a simple one for a working example).
Here is possible solution:
enum Delimiters { Delimiter };
const void* findData(size_t count) { return nullptr; }
template<typename...ARGS>
const void* findData(size_t count, size_t, ARGS...args)
{
return findData(count, args...);
}
template<typename...ARGS>
const void* findData(size_t count, const void* data, ARGS...args)
{
return count ? findData(count - 1, args...) : data;
}
template<typename...ARGS>
MyArray reordered(size_t count, Delimiters, ARGS...args)
{
return Concatenate(args...);
}
template<typename...ARGS>
MyArray reordered(size_t count, const void* size, ARGS...args)
{
return reordered(count, args...);
}
template<typename...ARGS>
MyArray reordered(size_t count, size_t size, ARGS...args)
{
return reordered(count + 1, args..., findData(count, args...), size);
}
template<typename...ARGS>
MyArray AutoConcatenate()
{
return reordered(0, ARGS::LAYOUT_SIZE..., ARGS::LAYOUT..., Delimiter);
}
If you know more elegant way, please let me know.
EDIT
One little more elegant way is to keep function argument count as template parameter...
I would like to write a class template that is used to contain sequences of elements. I am working on a method that will behave like push_back() in vector. This is what i have done so far:
template <class T, int N>
class mysequence{
T memblock[N];
public:
void setmember(T value);
T getmember(int x);
};
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
if (sizeof(memblock) == 1) memblock[0] = value;
else {
int y = sizeof(memblock) / sizeof(memblock[0]);
memblock[y] = value;
}
}
template <class T, int N>
T mysequence<T, N>::getmember(int x) {
return memblock[x];
}
int main()
{
mysequence < int, 14 > myints;
myints.setmember(9);
cout << myints.getmember(0);
}
And it returns:
-858993460
with error message. As far as i know the sizeof an empty class or structure is 1. But i also tried:
if (sizeof(memblock) == NULL) memblock[0] = value;
But all the same. I can not figure out what`s wrong with my code. If anyone has any ideas, i will apressiate it.
What is wrong is your use of sizeof. It does not tell you how many elements do you have, but just the memory used (N * sizeof(T)). To keep track of elements contained you seed a separate counter.
You are asking getmember() to return the first element of the array, but setmember() never fills in that element unless T is char, unsigned char, or bool, and N is 1. In any other combination, setmember() is storing the value past the end of the array. If you want to store a value in the last element, use N-1 instead (no need to calculate y as it will match N):
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
memblock[N-1] = value;
}
But, that is no use in a multi-element array. Since you want to mimic push_back(), you need to have setmember() store the value in the last available element instead:
template <class T, int N>
class mysequence{
T memblock[N];
int count;
public:
mysequence() : count(0) {}
void setmember(T value);
T getmember(int x);
};
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
if (count < N) {
memblock[count-1] = value;
++count;
}
else
throw length_error("sequence is full");
}
template <class T, int N>
T mysequence<T, N>::getmember(int x) {
if ((x < 0) || (x >= count))
throw out_of_range("x is out of range");
return memblock[x];
}
int main()
{
mysequence < int, 14 > myints;
myints.setmember(9);
cout << myints.getmember(0);
}
Or, you could simply get rid of your array and use a real vector instead:
template <class T>
class mysequence{
vector<T> memblock;
public:
void setmember(T value);
T getmember(int x);
};
template <class T>
void mysequence<T>::setmember(T value) {
memblock.push_back(value);
}
template <class T>
T mysequence<T>::getmember(int x) {
return memblock[x];
}
int main()
{
mysequence < int > myints;
myints.setmember(9);
cout << myints.getmember(0);
}
This should indicate the problem:
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
if (sizeof(memblock) == 1) memblock[0] = value;
else {
int y = sizeof(memblock) / sizeof(memblock[0]);
cout << "y: " << y << endl;
memblock[y] = value;
}
}
When called with:
myints.setmember(1);
myints.setmember(5);
myints.setmember(8);
I get the output:
y: 14
y: 14
y: 14
y is always the same because it's calculated by finding the length of the underlying storage array, not the number of elements actually stored in that array.
Given that we aren't filling in the elements of the array correctly the values we get when we call getmember will be pulling out uninitialized elements of the array. This is where you get the garbage values such as -858993460 from (note that on my machine I got a different value here which is in line with the UB we have here).
If you want to support a push_back type of method that doesn't require an index to be passed in with the value then you need to change your design to have a field that keeps track of how many elements you have inserted into the array so far.
Additionally I'd make some checks to see if there is enough space to actually put the newest element into the back of the array before doing it.
You have misunderstood the functionality of push_back. What push_back does is that it adds elements to the end of an array, even an empty array. So I have modified some functionality of your functions and indicated them in the comments.
#include <iostream>
#include <algorithm>
using namespace std;
template <class T, int N>
class mysequence{
T *memblock;//You will need a pointer for enabling the resizing of the array
int size;//This is used to keep track of the size of the array
public:
mysequence();
mysequence(const mysequence &src);
~mysequence();
void setmember(T value);
T getmember(int x);
void push_back(T value);
mysequence& operator=(const mysequence &src);
};
template <class T, int N>
void mysequence<T, N>::mysequence(){
size = N;
memblock = new T[N];
}
template <class T, int N>
void mysequence<T, N>::mysequence(const mysequence<T,N> &src){
size = src.size
memblock = new T[size];
copy(src.memblock, src.memblock+size, memblock);
}
template <class T, int N>
void mysequence<T, N>::~mysequence(){
delete[] memblock;
}
template <class T, int N>
void mysequence<T, N>::setmember(T value) {//this setmember function just sets the last element of the array to the value specified
if( size > 0 ){
memblock[size-1] = value;
}
}
template<class T, int N>
void mysequence<T,N>::push_back(T value){
T *pointer = new T[size+1];
copy(memblock, memblock+size, pointer);
pointer[size] = value;
delete[] arr;
arr = pointer;
size++;
}
template <class T, int N>
T mysequence<T, N>::getmember(int x) {
return memblock[x];
}
template<class T, int N>
mysequence<T,N>& mysequence<T,N>::operator=(const mysequence<T,N> &src){
T *pointer = new T[src.size];
copy(src.memblock, src.memblock+src.size, pointer);
delete[] arr;
arr = pointer;
size = src.size;
}
int main()
{
mysequence < int, 14 > myints;
myints.setmember(9);
cout << myints.getmember(13)<<endl;//the indexing is one less. so to access the last element, you need to specify 13. As other values are not initialized, they will have garbage value in them
myints.push_back(1);
cout<<myints.getmember(14)<<endl;
return 0;
}
I have a variadic template function that accepts std::function with variadic types. I want to find total sizeof of those types that std::function has, except that I want to treat double and float types as special, where sizeof(double) == 100 and sizeof(float) == 50.
Here is pseudo-code (doesn't compile)
#include <iostream>
#include <functional>
// terminating case for T=double
template<>
size_t getSize<double>()
{
return 100;
}
// terminating case for T=float
template<>
size_t getSize<float>()
{
return 50;
}
// terminating case for T being anything else
template<class T>
size_t getSize<T>()
{
return sizeof(T);
}
// recursive case
template<class T, class ...S>
size_t getSize<T, ...S>()
{
return getSize<T>() + getSize<S>();
}
template <class ...T>
void print_function_arg_custom_size(std::function<void(T...)> f)
{
size_t totalSize = 0;
// get size recursively
totalSize = getSize<T>();
std::cout << "totalSize: " << totalSize << std::endl;
}
void foo(uint8_t a, uint16_t b, uint32_t c, double d)
{
// noop
}
int main()
{
std::function<void(uint8_t, uint16_t, uint32_t, double)> f = foo;
print_function_arg_custom_size<uint8_t, uint16_t, uint32_t, double>(f);
return 0;
}
One of the issues I'm having with this is that getSize doesn't like my template specialization.
Couple of errors :
template<class T>
size_t getSize<T>()
{
return sizeof(T);
}
You don't need to provide a "specialization list" since it isn't a specialization. Also, since parameter packs can be empty, the compiler can't choose between the overloads getSize<T> and getSize<T, S...>. To fix it, make these changes:
template <typename T>
size_t getSize()
{
return sizeof(T);
}
// recursive case
template<class T, class U, class ...S>
size_t getSize()
{
return getSize<T>() + getSize<U, S...>();
}
Live Demo.