I want an array of objects prefixed with size/capacity. My requirements are:
The array elements should to be constructed on demand, like std::vector.
This object itself will be shared (i.e., heap allocated), so using std::vector instead would imply 2 levels of indirection and 2 allocations, which I have to avoid.
Requirements up to this point are non-negotiable. Here's my sample to get the rough idea of what I'd do:
#include <cassert>
#include <cstdio>
#include <string>
template <class T>
struct Array {
private:
int size_;
int capacity_;
alignas(T) unsigned char data_[];
Array() = default;
public:
Array(Array const&) = delete;
Array& operator=(Array const&) = delete;
static auto newArr(int capacity) {
auto p = new unsigned char[sizeof(Array) + capacity * sizeof(T)];
auto pObj = new (p) Array;
pObj->size_ = 0;
pObj->capacity_ = capacity;
return pObj;
}
static auto deleteArr(Array* arr) {
if (!arr) return;
for (int i = 0; i != arr->size_; ++i) arr->get(i).~T();
arr->~Array();
delete[] reinterpret_cast<unsigned char*>(arr);
}
auto& get(int index) {
return reinterpret_cast<T&>(data_[index * sizeof(T)]);
}
auto push_back(T const& t) {
assert(size_ < capacity_);
new (&get(size_++)) T(t);
}
};
int main() {
auto arr = Array<std::string>::newArr(5);
for (int i = 0; i != 3; ++i) {
arr->push_back(std::to_string(i));
}
for (int i = 0; i != 3; ++i) {
std::printf("arr[%d] = %s\n", i, arr->get(i).c_str());
}
Array<std::string>::deleteArr(arr);
}
It uses a flexible-array-member, which is an extension and that's OK by me (works in GCC and clang? then it's OK). But it is not constexpr friendly because it necessarily uses:
placement new, not allowed in constexpr context for some reason, even though that's surely what allocators do. We can't replace it with an allocator because they don't support the flexible array member trick.
reinterpret_cast to access the elements as T and to free the memory at the end.
My question:
How do I satisfy the previously mentioned requirements and keep the class constexpr friendly?
Ultimately, what you're trying to do is create a contiguous series of objects in unformed memory that isn't defined by a single, valid C++ struct or by a C++ array of Ts. Constexpr allocations cannot do that.
You can allocate a byte array in constexpr code. But you cannot subsequently do any of the casting that normal C++ would require in order to partition this memory into a series of objects of different types. You can allocate storage suitable for an array of Array<T> objects. Or you can allocate storage suitable for an array of T objects. But std::allocator<T>::allocate will always return a T*. And constexpr code doesn't let you cast this pointer to some other, unrelated type.
And without being able to do this cast, you cannot later call std::construct_at<T>, since the template parameter T must match the pointer type you give it.
This is of course by design. Every constexpr allocation of type T must contain zero or more Ts. That's all it can contain.
Considering #NicolBolas's answer, this can't be done as is, but if you can afford an indirection, it can be done. You do separate allocations if the object is constructed at compile-time where performance concern doesn't exist, and do the single-allocation + reinterpret_cast trick if constructed at runtime:
#include <cassert>
#include <new>
#include <type_traits>
#include <memory>
template <class T>
struct ArrayData {
protected:
int size_ = 0;
int capacity_ = 0;
T *buffer;
};
template <class T>
struct alignas(std::max(alignof(T), alignof(ArrayData<T>))) Array
: ArrayData<T> {
private:
constexpr Array() = default;
using alloc = std::allocator<T>;
using alloc_traits = std::allocator_traits<alloc>;
public:
Array(Array const &) = delete;
Array &operator=(Array const &) = delete;
constexpr static auto newArr(int capacity) {
if (std::is_constant_evaluated()) {
auto arr = new Array<T>();
alloc a;
T *buffer = alloc_traits::allocate(a, capacity);
arr->capacity_ = capacity;
arr->buffer = buffer;
return arr;
} else {
auto p = new unsigned char[sizeof(Array) + capacity * sizeof(T)];
auto pObj = new (p) Array;
pObj->capacity_ = capacity;
pObj->buffer = std::launder(reinterpret_cast<T *>(pObj + 1));
return pObj;
}
}
constexpr static auto deleteArr(Array *arr) noexcept {
if (!arr) return;
auto p = arr->buffer;
for (int i = 0, size = arr->size_; i != size; ++i)
std::destroy_at(p + i);
if (std::is_constant_evaluated()) {
auto capacity = arr->capacity_;
delete arr;
alloc a;
alloc_traits::deallocate(a, p, capacity);
} else {
arr->~Array();
delete[] reinterpret_cast<unsigned char *>(arr);
}
}
constexpr auto &get(int index) { return this->buffer[index]; }
constexpr auto push_back(T const &t) {
assert(this->size_ < this->capacity_);
std::construct_at(this->buffer + this->size_++, t);
}
};
constexpr int test() {
auto const size = 10;
auto arr = Array<int>::newArr(size);
for (int i = 0; i != size; ++i) arr->push_back(i);
int sum = 0;
for (int i = 0; i != size; ++i) sum += arr->get(i);
Array<int>::deleteArr(arr);
return sum;
}
int main() {
int rt = test();
int constexpr ct = test();
return rt == ct;
}
Here's another approach. I've used unions to make it possible for subobjects of a single array object to have different types and different lifetimes. I'm not entirely sure if everything's strictly legal, but the major compilers don't complain.
I did need to have newArr return an object rather than pointer. If that's not an issue, it would probably make more sense to just give FlexArray an actual constructor and destructor.
#include <memory>
#include <cassert>
template <typename T>
class FlexArray
{
public:
FlexArray(const FlexArray&) = delete;
FlexArray& operator=(const FlexArray&) = delete;
static constexpr FlexArray newArr(unsigned int capacity);
constexpr void deleteArr();
constexpr unsigned int size() const;
constexpr unsigned int capacity() const;
constexpr T& get(unsigned int index);
constexpr void push_back(T const& obj);
private:
struct Header {
unsigned int size;
unsigned int capacity;
};
static constexpr auto T_per_node =
(sizeof(T) < sizeof(Header)) ? sizeof(Header)/sizeof(T) : 1U;
union MaybeT {
unsigned char dummy;
T obj;
constexpr MaybeT() {}
explicit constexpr MaybeT(const T& src) : obj(src) {}
constexpr ~MaybeT() {}
};
union U {
Header head;
MaybeT data[T_per_node];
constexpr U() : data{} {}
constexpr ~U() {}
};
U* nodes;
explicit constexpr FlexArray(U* n) : nodes(n) {}
};
template <typename T>
constexpr FlexArray<T> FlexArray<T>::newArr(unsigned int capacity)
{
auto new_nodes = new U[1 + (capacity + (T_per_node-1))/T_per_node];
new_nodes[0].head = {0, capacity};
return FlexArray{new_nodes};
}
template <typename T>
constexpr void FlexArray<T>::deleteArr()
{
unsigned int i = size();
while (i--) {
get(i).~T();
}
delete[] nodes;
nodes = nullptr;
}
template <typename T>
constexpr unsigned int FlexArray<T>::size() const
{
return nodes[0].head.size;
}
template <typename T>
constexpr unsigned int FlexArray<T>::capacity() const
{
return nodes[0].head.capacity;
}
template <typename T>
constexpr T& FlexArray<T>::get(unsigned int index)
{
return nodes[1 + index / T_per_node].data[index % T_per_node].obj;
}
template <typename T>
constexpr void FlexArray<T>::push_back(const T& obj)
{
assert(size() < capacity());
auto index = nodes[0].head.size++;
MaybeT *addr = nodes[1 + index / T_per_node].data + (index % T_per_node);
addr->~MaybeT();
std::construct_at(addr, obj);
}
#include <functional>
constexpr int test()
{
int a = -1;
int b = 2;
int c = 5;
auto arr = FlexArray<std::reference_wrapper<int>>::newArr(3);
arr.push_back(std::ref(a));
arr.push_back(std::ref(b));
arr.push_back(std::ref(c));
arr.get(1).get() = 7;
auto sum = arr.get(0) + b + arr.get(2);
arr.deleteArr();
return sum;
}
static_assert(test() == 11);
See it on godbolt.
Related
I am trying to code a template array class and overloading some operators. Part of my code is as follows:
template.h:
main.cpp:
C2679 binary '<': no operator found which takes a right-hand operand of type 'Array<int>' (or there is no acceptable conversion)
What is causing this error?
What is causing this error?
You are using
return this < a;
this is a pointer while a is a reference to an object. It's analgous to comparing an int* with an int.
int a = 10;
int b = 11;
int* p = &b;
if ( p < a ) { ... }
That is not right.
That function needs to be implemented differently. You need to compare each item of the arrays and return an appropriate value.
template<typename T>
bool Array<T>::operator<(const T& a)
{
int lowerLength = std::min(this->arrLength, a.arrLengh);
for ( int i = 0; i < lowerLength; ++i )
{
if ( this->myArray[i] != a.myArray[i] )
{
return (this->myArray[i] < a.myArray[i]);
}
}
// If we get here, return a value based on which array has more elements.
return (this->arrLength < a.arrLengh)
}
While at it, make the member function a const member function.
bool Array<T>::operator<(const T& a) const;
and change the implementation accordingly.
In findBigPos() (and your other functions in Driver.cpp, too), you should be passing arr by reference, not by pointer. When arr is a pointer, arr[index] is the same as *(arr + index) - it performs pointer arithmetic to dereference the pointer at a given offset, it does not index into your array at all. That is why the compiler thinks you are comparing Array<int> objects, and not calling your operator[].
Try this instead:
#include "wallet.h"
#include "currency.h"
#include "array.h"
#include <iostream>
#include <string>
using namespace std;
template<typename T>
void recurSelectionSort(Array<T>&, int size, int index);
template<typename T>
int findBigPos(Array<T>&, int size, int index);
int main() {
//code
}
template<typename T>
void recurSelectionSort(Array<T>& arr, int size, int index) // move the biggest element in arr to index
{
if (index == size) {
return;
}
else if (index < size) {
int bigPos = findBigPos(arr, size, index); //position of "biggest" element
T bigVal = arr[bigPos]; //the value of "biggest" element
T copy = arr[index]; //copy of wat ever is going to get copy
arr[index] = bigVal;
arr[bigPos] = copy;
recurSelectionSort(arr, size, index + 1);
cout << arr;
}
}
template<typename T>
int findBigPos(Array<T>& arr, int size, int index)
{
if (index == size - 1) {
return index;
}
else
{
int bigPos = findBigPos(arr, size, index + 1);
return arr[bigPos] < arr[index] ? index : bigPos;
}
}
That said, there are some issues with your Array class itself, too.
You are not implementing the Rule of 3/5/0. Your class is lacking a copy constructor and a copy assignment operator, and in C++11 and later a move constructor and a move assignment operator.
you don't have a const version of your operator[] for your operator<< to use, since it takes a reference to a const Array<T> as input.
your operator[] is not checking for index < 0. And it would be better to throw a std::out_of_range exception instead of an int. If it throws at all. Typically, an array's operator[] should not perform bounds checking at all. That is why containers like std::vector and std::string have a separate at() method for handling bounds checking.
your operator< is not implemented correctly at all. You can't compare a Array<T>* pointer to a const T& reference. You probably meant to dereference the this pointer before comparing it to a, but then that would lead to an endless recursive loop. What you should do instead is change const T& a to const Array<T> &a and then compare the contents of this to the contents of a.
Try this:
#ifndef ARRAY_HEADER
#define ARRAY_HEADER
#include <iostream>
#include <stdexcept>
#include <utility>
template<typename T>
class Array
{
private:
int arrLength;
T* myArray;
public:
Array(int length = 5);
Array(const Array &a);
Array(Array &&a);
virtual ~Array();
int getLength() const;
Array& operator=(Array a);
T& operator[](int index);
const T& operator[](int index) const;
bool operator<(const Array &a) const;
friend std::ostream& operator<<(std::ostream &output, const Array &arr)
{
int arrSize = arr.getLength();
for (int i = 0; i < arrSize; i++) {
output << arr[i] << " ";
}
return output;
}
};
template<typename T>
Array<T>::Array(int length)
{
myArray = new T[length];
arrLength = length;
}
template<typename T>
Array<T>::Array(const Array<T> &a)
{
myArray = new T[a.arrLength];
arrLength = a.arrLength;
for(int i = 0; i < arrLength; ++i)
myArray[i] = a.myArray[i];
}
template<typename T>
Array<T>::Array(Array<T> &&a)
{
arrLength = a.arrLength;
myArray = a.myArray;
a.myArray = nullptr;
a.arrLength = 0;
}
template<typename T>
Array<T>::~Array()
{
delete[] myArray;
}
template<typename T>
int Array<T>::getLength() const
{
return arrLength;
}
template<typename T>
Array<T>& Array<T>::operator=(Array<T> a)
{
using std::swap;
swap(myArray, a.myArray);
swap(arrLength, a.arrLength);
return *this;
}
template<typename T>
T& Array<T>::operator[](int index) {
if ((index < 0) || (index >= arrLength)) {
throw std::out_of_range("index is out of range");
}
return myArray[index];
}
template<typename T>
const T& Array<T>::operator[](int index) const {
if ((index < 0) || (index >= arrLength)) {
throw std::out_of_range("index is out of range");
}
return myArray[index];
}
template<typename T>
bool Array<T>::operator<(const Array<T> &a) const
{
if (arrLength < a.arrLength)
return true;
if (arrLength == a.arrLength)
{
for (int i = 0; i < arrLength; ++i)
{
if (myArray[i] != a.myArray[i])
return myArray[i] < a.myArray[i];
}
}
return false;
}
#endif
I have a template class foo (essentially a matrix). The template parameter for foo can only be int, float, or double, i.e., not const int. The reason for this is that I have specialized operators for these, and it seems redundant to duplicate the operators for the const cases. I have two get_data functions, which return a pointer with appropriate constness. But I also wan't a row function that selects a single row and returns a const foo, such that the caller cannot modify the returned object.
My questions:
1) How can i make B const?
2) Should I make operator functions for e.g. foo ?
2) Should I make a reference_foo class?
template <class T>
class foo
{
using uint8_t = unsigned char;
int rows = 0;
int cols = 0;
T * data = nullptr;
bool reference = false;
public:
foo() = default;
//foo(const foo&) // this is not included here for simplicity
//foo& operator=(const foo&) // this is not included here for simplicity
foo(int r, int c) : rows(r), cols(c)
{
data = new T[rows * cols];
}
~foo()
{
if (!reference)
{
delete[] data;
}
}
T * get_data()
{
return data;
}
T const * get_data() const
{
return data;
}
const foo row(int r) const
{
foo t;
t.rows = 1;
t.cols = cols;
t.reference = true;
// t.data = get_data() + r * cols; // ERROR: invalid conversion from 'const uint8_t*' to 'uint8_t*'
t.data = const_cast<T*>(get_data()) + r * cols; // Not pretty, but "ok" if the returned object is const
return t;
}
};
int main()
{
const foo<int> A(2, 1);
// A.get_data()[0] = 1; // ERROR: assignment of read-only location, perfectly catched by compiler
auto B = A.row(1);
B.get_data()[0] = 1; // B is not const... overwritten...
return 0;
}
The operator functions has been left out for simplicity.
There are 2 kinds of constness here. Const data and const handle.
What we want to do is create sanity out of the four combinations:
const handle, const data = const
const handle, mutable data = const
mutable handle, const data = const
mutable handle, mutable data = mutable
Furthermore, marking a return value as const has no meaning. A return value is an r-value. It will be either copied or moved. This will not result in a const handle at the call site.
So we need to detect constness in 2 places in respect of get_data(). C++ does the first for us with a const overload. Then we must defer to another template which is evaluated in deduced context so we can use std::enable_if:
#include <cstddef>
#include <utility>
#include <type_traits>
// default getter - element != const element
template<class Element, typename = void>
struct data_getter
{
using element_type = Element;
using const_element_type = std::add_const_t<element_type>;
// detect mutable container
element_type* operator()(element_type ** pp) const
{
return *pp;
}
// detect const container
const_element_type* operator()(element_type * const * pp) const
{
return *pp;
}
};
// specific specialisation for element == const element
template<class Element>
struct data_getter<Element,
std::enable_if_t<
std::is_same<Element, std::add_const_t<Element>>::value>>
{
// in this case the container's constness is unimportant, so
// we use const because it means only writing one method
Element* operator()(Element *const* p) const
{
return *p;
}
};
template <class T>
class foo
{
public:
using element = T;
using const_element = std::add_const_t<element>;
int rows = 0;
int cols = 0;
element * data = nullptr;
bool reference = false;
public:
foo() = default;
//foo(const foo&) // this is not included here for simplicity
//foo& operator=(const foo&) // this is not included here for simplicity
foo(int r, int c) : rows(r), cols(c)
{
data = new element[rows * cols];
}
~foo()
{
if (!reference)
{
delete[] data;
}
}
decltype(auto) get_data()
{
// defer to getter
return data_getter<element>()(&data);
}
decltype(auto) get_data() const
{
// defer to getter
return data_getter<const_element>()(&data);
}
// this will return a mutable container of const data
foo<const_element> row(int r) const
{
foo<const_element> t;
t.rows = 1;
t.cols = cols;
t.reference = true;
t.data = get_data() + r * cols;
return t;
}
};
int main()
{
foo<int> A(2, 1);
A.get_data()[0] = 1;
auto AC = A.row(0);
auto x = AC.get_data()[0]; // fine
// AC.get_data()[0] = 1; // assignment of read-only location
return 0;
}
This is probably a duplicate, but I cannot find the solution anywhere.
I have source code like this:
struct Blob{
//...
static void *operator new(size_t size_reported, size_t size) {
return ::operator new(size);
}
};
I use it like this:
std::shared_ptr<Blob> blob;
// ...
size_t size = calcSize(); // it returns say 231
Blob *p = new(size) Blob();
blob.reset(p);
Can I change the code somehow so I can use std::make_shared or std::allocate_shared so I have single allocation instead of two allocations?
Update
I was able to eliminate the new and simplify the code to the following:
struct Blob{
//...
};
std::shared_ptr<Blob> blob;
// ...
size_t size = calcSize(); // it returns say 231
// allocate memory
void *addr = ::operator new(size);
// placement new
Blob *p = ::new(addr) Blob();
blob.reset(p);
It does exactly the same thing, but I think now it is more clear what I'm trying to do.
Here is what come up with.
Since there is no way to pass size to the allocator, you can do it via global variable or class member.
In both cases the solution is not elegant at all and is rather dangerous - disaster waiting to happen now or later when code needs to be maintained.
Another unexpected problem may happen if allocate_shared place the shared_ptr control block after the buffer class.
In such case there will be clear buffer overflow, because sizeof(buffer) will report size like 1 byte or so.
Once again - the code is working, but will have issues in the future for sure.
#include <stdio.h>
#include <string.h>
#include <memory>
// ============================
class buffer{
public:
buffer(const char *s){
strcpy(x, s);
}
char x[1];
};
// ============================
template <typename T>
struct buffer_allocator {
using value_type = T;
buffer_allocator() = default;
template <class U>
buffer_allocator(const buffer_allocator<U>&) {}
T* allocate(size_t n) {
void *p = operator new(n * sizeof(T));
printf("Allocate %p, (%zu)\n", p, get_size());
return (T*) p;
}
void deallocate(T* p, size_t n) {
delete p;
printf("Deallocate %p, (%zu)\n", p, get_size());
}
size_t get_size(){
return size;
}
void set_size(size_t size){
this->size = size;
}
private:
size_t size = 0;
};
template <typename T, typename U>
inline bool operator == (const buffer_allocator<T>&, const buffer_allocator<U>&) {
return true;
}
template <typename T, typename U>
inline bool operator != (const buffer_allocator<T>& a, const buffer_allocator<U>& b) {
return !(a == b);
}
// ============================
int main(int argc, char *argv[]){
buffer_allocator<buffer> ba;
const char *s = "hello world!";
ba.set_size( strlen(s) + 1 );
auto b = std::allocate_shared<buffer>(ba, s);
printf("Pointer %p\n", b.get());
printf("%s\n", b->x);
printf("%zu\n", b.use_count());
auto b1 = b;
printf("%zu\n", b1.use_count());
return 0;
}
I am getting an unresolved external compile error when compiling my template classes. I have separated the code into .h and .cpp files.
I read some posts and now I understand that this will not work due to linking issues as explained in this post.
include the full definition of the member function in the template's header file and not have a source file for the template,
define all the member functions in the template's source file as "inline", or
define the member functions in the template's source with the "export" keyword. Unfortunately this isn't supported by a lot of compilers.
However, none of these options will work since I have conversion functions between a number of these template functions (which results in a cross include compile issue).
How do I fix this issue?
EDIT: Include Code
StaticArray.h
template<typename T>
class Array;
template<typename T, unsigned int N>
class StaticArray
{
protected:
T* _data[N];
public:
StaticArray();
StaticArray(const StaticArray<T,N>& other);
~StaticArray();
void Release(unsigned int index);
void Release(T* data);
void ReleaseAll();
Array<T> ToArray();
bool operator == (const StaticArray<T,N>& other);
bool operator != (const StaticArray<T,N>& other);
T*& operator [] (unsigned int index) const;
StaticArray<T,N>& operator = (const StaticArray<T,N>& other);
};
StaticArray.cpp
#pragma region StaticArray::CoreMethods
template<typename T, unsigned int N>
StaticArray<T, N>::StaticArray()
{
for (unsigned int i = 0; i < N; i++)
{
this->_data[i] = null;
}
}
template<typename T, unsigned int N>
StaticArray<T, N>::StaticArray(const StaticArray<T,N>& other)
{
for (unsigned int i = 0; i < N; i++)
{
this->_data[i] = other._data[i];
}
}
template<typename T, unsigned int N>
StaticArray<T, N>::~StaticArray()
{
}
#pragma endregion
#pragma region StaticArray::Methods
template<typename T, unsigned int N>
void StaticArray<T,N>::Release(unsigned int index)
{
if (index < N)
{
delete this->_data[i];
this->_data[i] = null;
}
else
{
throw new Exception::IndexOutOfBoundsException("StaticArray accessed at index greater than N.");
}
}
template<typename T, unsigned int N>
void StaticArray<T,N>::Release(T* data)
{
if (data == null)
{
throw new Exception::InvalidArgumentException("StaticArray Release call argument must not be null.");
}
for (unsigned int i = 0; i < N; i++)
{
if (this->_data[i] == data)
{
this->Release(i);
return;
}
}
throw new Exception::InvalidArgumentException("StaticArray Release call argument was not in the array.");
}
template<typename T, unsigned int N>
void StaticArray<T,N>::ReleaseAll()
{
for (unsigned int i = 0; i < N; i++)
{
if (this->_data[i] != null)
{
delete this->_data[i];
this->_data[i] = null;
}
}
}
template<typename T, unsigned int N>
Array<T> StaticArray<T,N>::ToArray()
{
Array<T> ret(N);
for (unsigned int i = 0; i < N; i++)
{
ret[i] = this->_data[i];
}
return ret;
}
#pragma endregion
#pragma region StaticArray::OperatorOverloads
template<typename T, unsigned int N>
bool StaticArray<T,N>::operator == (const StaticArray<T,N>& other)
{
for (unsigned int i = 0; i < N; i++)
{
if (this->_data[i] != other._data[i])
{
return false;
}
}
return true;
}
template<typename T, unsigned int N>
bool StaticArray<T,N>::operator != (const StaticArray<T,N>& other)
{
return !((*this) == other);
}
template<typename T, unsigned int N>
T*& StaticArray<T, N>::operator[](unsigned int index) const
{
if (index < N)
{
return this->_data[index];
}
else
{
throw new Exception::IndexOutOfBoundsException("StaticArray accessed at index greater than N.");
}
}
template<typename T, unsigned int N>
StaticArray<T, N>& StaticArray<T, N>::operator = (const StaticArray<T,N>& other)
{
for (unsigned int i = 0; i < N; i++)
{
this->_data[i] = other._data[i];
}
return *this;
}
main.cpp
#include "StaticArray.h"
#include "Array.h"
int main(int argc, char** argv[])
{
StaticArray<int,5> sar;
sar[0] = new int(1);
sar[1] = new int(2);
sar[2] = new int(3);
sar[3] = new int(4);
sar[4] = new int(5);
return 0;
}
You don't have to have a definition of class Array to use class StaticArray except at the point you actually call ToArray. So it doesn't matter in which order the templates and their functions are declared, just as long as each one forward-declares the classes it uses.
Explicit template instantiation. Add this line to the bottom of StaticArray.cpp:
template class StaticArray<int,5>;
I see the forward declaration for class Array at the beginning of StaticArray.h. In your main.cpp try swapping the lines to
#include "Array.h"
#include "StaticArray.h"
from a quick glance it seems like the compiler hasnt seen Array when its building StaticArray and hence unresolved external?
I eventually managed to figure out the best way to make this functionality work in a structured manner.
You need to remove the conversions (such as StaticArray to Array) from the StaticArray class and instead have both StaticArray and Array included in another code file that contains utility functions (such as conversions).
The key here is to keep the includes working upwards. That is: utility.h (for example) includes Array.h and StaticArray.h instead of them including one another.
Then you are free to put all definition code into the StaticArray.h file with no cross-include side-effects (and thus fix the unresolved external).
i`m working on my assignment for univ, and since some parts are not really good explained i got some problems there is my structure and my constructor for it, it has to be dynamical but i get the fallowing error. Some help is really appreciated thank you.
.h:
const int days=31;
const int exp=6;
struct Array{
int days;
int exp;
int **M;
};
.cpp:
void constr(Array &loc){
//Construct of 31*6 Matrix, were 31 nr. of days and 6 specific types:
//0-HouseKeeping, 1-Food, 2-Transport, 3-Clothing, 4-TelNet, 5-others
loc.days = days;
loc.exp = exp;
loc.M=malloc(loc.days*sizeof(int*));
for(int i=0; i<loc.days;i++ ){
loc.M[i] = malloc(loc.exp*sizeof(int));
for (int j = 0; j< loc.exp; j++){
loc.M[i][j] = 0;
}
}
}
error:
..\src\structs.cpp: In function 'void constr(Array&)':
..\src\structs.cpp:7:36: error: invalid conversion from 'void*' to 'int**' [-fpermissive]
..\src\structs.cpp:9:40: error: invalid conversion from 'void*' to 'int*' [-fpermissive]
Since you asked for C++ constructors in your comment... See the code below. I also replaced your two-dimensional C-style array with a C++ vector. I added code comments to the relevant lines:
Array.h:
#pragma once
#include <vector>
struct Array
{
// this is a c++ constructor declaration
Array(int daysParam, int expParam);
int days;
int exp;
// use a vector of vectors instead allocating with new or malloc
// it is easier to initialize and the compiler will clean it up for you
std::vector<std::vector<int> > M;
};
Array.cpp:
#include "Array.h"
// Array constructor definition with initializer list
// all data members are initialized here by invoking their constructor
Array::Array(int daysParam, int expParam)
: days(daysParam),
exp(expParam),
M(daysParam, std::vector<int>(expParam, 0))
{
}
Example for usage of Array (Program.cpp):
#include "Array.h"
int main()
{
// create a new Array, using the c++ constructor
Array myArray(31, 6);
// access elements in the 2-dimensional array
int singleValue = myArray.M[15][3];
return 0;
}
I strongly advise you to read a book about C++
Since this is C++:
loc.M = new int*[loc.days];
for(int i=0; i<loc.days;i++ ){
loc.M[i] = new int[loc.exp];
for (int j = 0; j< loc.exp; j++){
loc.M[i][j] = 0;
}
}
loc.M = (int**)malloc(loc.days*sizeof(int*));
loc.M[i] = (int*)malloc(loc.exp*sizeof(int));
Please, stop using std::vector > or, worse T tab[][] for representing a 2D array. You should use a 1D array to store data, a an index array to store row pointers. That way, your data remains contiguous, and you still can have a nice syntax.
template<typename T>
class Array2D
{
std::vector<T> m_data;
std::vector<T*> m_ptr;
size_t m_iWidth;
size_t m_iHeight;
void Link(void)
{
for (unsigned int j = 0; j < m_iHeight; ++j)
m_ptr[j] = &m_data[j * m_iWidth];
}
public:
Array2D(void)
{
};
Array2D(const size_t i_width, const size_t i_height) :
m_iWidth(i_width),
m_iHeight(i_height),
m_data(i_width * i_height),
m_ptr(i_height)
{
Link();
}
void Resize(const size_t niou_width, const size_t niou_height)
{
if (m_iWidth == niou_width && m_iHeight == niou_height)
return;
m_iWidth = niou_width;
m_iHeight = niou_height;
m_data.resize(niou_height * niou_width);
m_ptr.resize(niou_height);
Link();
}
typename std::vector<T>::iterator begin(void)
{
return m_data.begin();
}
typename std::vector<T>::iterator end(void)
{
return m_data.end();
}
void assign(T value)
{
m_data.assign(m_iWidth * m_iHeight, value);
}
Array2D(const Array2D& a) :
m_iWidth(a.m_iWidth),
m_iHeight(a.m_iHeight),
m_data(a.m_data)
{
m_ptr.resize(m_iHeight);
Link();
}
Array2D& operator=(const Array2D a)
{
swap(*this, a);
return *this;
}
template <typename U>
friend void swap(Array2D<U>& first, Array2D<U>& second)
{
using std::swap;
swap(first.m_iHeight, second.m_iHeight);
swap(first.m_iWidth, second.m_iWidth);
swap(first.m_data, second.m_data);
swap(first.m_ptr, second.m_ptr);
}
~Array2D()
{
};
T* operator[](const size_t ligne)
{
return m_ptr[ligne];
};
const T* operator[](const size_t ligne) const
{
return m_ptr[ligne];
};
T& operator()(const size_t col, const size_t lig)
{
return m_ptr[lig][col];
};
const T& operator()(const size_t col, const size_t lig) const
{
return m_ptr[lig][col];
};