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;
}
Related
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.
Is there an std container for C-style arrays with variable size?
For example, I have the following code
int size = 5; // This is not a constant in general
int *my_array = SpecialAllocationFunction(size);
I want to be able to access this array with a C++, std-style container. Something that has iterators and functions like: size, begin, end, ...
I know that I can use std::array if my_array has a constant size. I also can write one myself, but I feel there bound to be something ready-made.
Using a custom allocator, it is possible to build a vector with a size known only at run time (so no std::array possible), wrapping an existing array. It is even possible to keep pre-existing values by overriding the special construct method(*).
Here is a possible implementation:
/**
* a pseudo allocator which receives in constructor an existing array
* of a known size, and will return it provided the required size
* is less than the declared one. If keep is true in contructor,
* nothing is done at object construction time: original values are
* preserved
* at deallocation time, nothing will happen
*/
template <class T>
class SpecialAllocator {
T * addr;
size_t sz;
bool keep;
public:
typedef T value_type;
SpecialAllocator(T * addr, size_t sz, bool keep):
addr(addr), sz(sz), keep(keep) {}
size_t max_size() {
return sz;
}
T* allocate(size_t n, const void* hint=0) {
if (n > sz) throw std::bad_alloc(); // throws a bad_alloc...
return addr;
}
void deallocate(T* p, size_t n) {}
template <class U, class... Args>
void construct(U* p, Args&&... args) {
if (! keep) {
::new((void *)p) U(std::forward<Args>(args)...);
}
}
template <class U>
void destroy(U* p) {
if (! keep) {
p->~U(); // do not destroy what we have not constructed...
}
}
};
It can then be used that way:
int size = 5; // This is not a constant in general
int *my_array = SpecialAllocationFunction(size);
SpecialAllocator<int> alloc(my_array, size);
std::vector<int, SpecialAllocator<int> > vec(size, alloc);
From that point, vec will be a true std::vector wrapping my_array.
Here is a simple code as demo:
int main(){
int arr[5] = { 5, 4, 3, 2, 1 };
SpecialAllocator<int> alloc(arr, 5, true); // original values will be preserved
std::vector<int, SpecialAllocator<int> > vec(5, alloc);
for(auto it= vec.begin(); it != vec.end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
try {
vec.push_back(8);
}
catch (std::bad_alloc& a) {
std::cout << "allocation error" << std::endl;
}
return 0;
}
It will successfully output:
5 4 3 2 1
allocation error
(*) BEWARE: Construction/destruction may be involved in different places: push_back, emplace_back, etc. Really think twice about your real use case before using no-op construct and destroy methods.
As #NathanOliver and #utnapistim said in the comments, gsl::span works. Since I don't want to include this library I ended up writing a "trivial wrapper" myself. Included below for others looking for an answer (and my future self)
template<class T>
class span {
public:
inline span() : _data(0), _size(0) {}
inline span(T* d, size_t s) : _data(d), _size(s) {}
inline T& operator[](size_t index) { return _data[index]; }
inline const T& operator[](size_t index) const { return _data[index];}
inline size_t size() const { return _size; };
inline T* begin() { return _data; }
inline const T* begin() const { return _data; }
inline T* end() { return _data+_size; }
inline const T* end() const { return _data+_size; }
protected:
T* _data;
size_t _size;
};
I have heard people say that "C++ doesn't need placement delete because it wouldn't do anything."
Consider the following code:
#include <cstdlib>
#include <cstdio>
#include <new>
////////////////////////////////////////////////////////////////
template<typename T, typename... ARGS>
T* customNew1(ARGS&&... args) {
printf("customNew1...\n");
auto ret = new T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
template<typename T>
void customDelete1(T *ptr) {
printf("customDelete1...\n");
delete ptr;
printf("OK\n\n");
}
////////////////////////////////
template<typename T, typename... ARGS>
T* customNew2(ARGS&&... args) {
printf("customNew2 alloc...\n");
void *buf = std::malloc(sizeof(T));
printf("customNew2 construct...\n");
auto ret = ::new(buf) T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
template<typename T>
void customDelete2(T *ptr) {
printf("customDelete2 destruct...\n");
// what I want: a "placement delete" which calls the destructor and returns the address that should be passed to the deallocation function
// e.g.
//
// void* ptrToFree = ::delete(ptr);
// std::free(ptrToFree);
//
// equally fine would be a "magic" operator that allows one to obtain said address without actually calling the destructor:
//
// void* ptrToFree = get_deallocation_address_of(ptr);
// ptr->~T();
// std::free(ptrToFree);
ptr->~T();
printf("customDelete2 free...\n");
std::free(ptr);
printf("OK\n\n");
}
////////////////////////////////////////////////////////////////
struct A {
int a;
A() : a(0) {
printf("A()\n");
}
virtual ~A() {
printf("~A()\n");
}
};
struct B {
int b;
B() : b(0) {
printf("B()\n");
}
virtual ~B() {
printf("~B()\n");
}
};
struct C : A, B {
int c;
C() : c(0) {
printf("C()\n");
}
~C() {
printf("~C()\n");
}
};
////////////////////////////////////////////////////////////////
int main() {
C *c1 = customNew1<C>();
A *a1 = c1;
B *b1 = c1;
// Assume c and a will be the same but b is offset
printf("c: %x\n", c1);
printf("a: %x\n", a1);
printf("b: %x\n", b1);
printf("\n");
customDelete1(b1); // <- this will work, the delete expression offsets b1 before deallocing
printf("--------------\n\n");
C *c2 = customNew2<C>();
A *a2 = c2;
B *b2 = c2;
printf("c: %x\n", c2);
printf("a: %x\n", a2);
printf("b: %x\n", b2);
printf("\n");
// customDelete2(b2); // <- this will break
customDelete2(a2); // <- this will work because a2 happens to point at the same address as c2
printf("--------------\n\n");
return 0;
}
As you can see here the destructors, being virtual, are all called properly, but the deallocation of b2 will still fail because b2 points at a different address than c2.
Note that a similar problem arises when one uses placement new[] to construct an array of objects, as described here:
Global "placement" delete[]
However this can be worked around without much trouble by simply saving the array size at the head of your block of memory and handling the array constructor/destructor calls manually in a loop using single object placement new/explicit destructor calls.
On the other hand, I cannot think of any graceful way to solve the problem with multiple inheritance. The "magic" code which retrieves the original pointer from the base pointer within the delete expression is implementation specific, and there's no simple way of "doing it manually" like you can with arrays.
Here is another situation where this becomes a problem, with an ugly hack to work around it:
#include <cstdlib>
#include <cstdio>
#include <new>
////////////////////////////////////////////////////////////////
// imagine this is a library in which all allocations/deallocations must be handled by this base interface
class Alloc {
public:
virtual void* alloc(std::size_t sz) =0;
virtual void free(void *ptr) =0;
};
// here is version which uses the normal allocation functions
class NormalAlloc : public Alloc {
public:
void* alloc(std::size_t sz) override final {
return std::malloc(sz);
}
void free(void *ptr) override final {
std::free(ptr);
}
};
// imagine we have a bunch of other versions like this that use different allocation schemes/memory heaps/etc.
class SuperEfficientAlloc : public Alloc {
void* alloc(std::size_t sz) override final {
// some routine for allocating super efficient memory...
(void)sz;
return nullptr;
}
void free(void *ptr) override final {
// some routine for freeing super efficient memory...
(void)ptr;
}
};
// etc...
////////////////////////////////
// in this library we will never call new or delete, instead we will always use the below functions
// this is used instead of new...
template<typename T, typename... ARGS>
T* customNew(Alloc &alloc, ARGS&&... args) {
printf("customNew alloc...\n");
void *buf = alloc.alloc(sizeof(T));
printf("customNew construct...\n");
auto ret = ::new(buf) T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
// um...
thread_local Alloc *stupidHack = nullptr;
// unfortunately we also have to replace the global delete in order for this hack to work
void operator delete(void *ptr) {
if (stupidHack) {
// the ptr that gets passed here is pointing at the right spot thanks to the delete expression below
// alloc has been stored in "stupidHack" since it can't be passed as an argument...
printf("customDelete free # %x...\n", ptr);
stupidHack->free(ptr);
stupidHack = nullptr;
} else {
// well fug :-D
}
}
// ...and this is used instead of delete
template<typename T>
void customDelete(Alloc &alloc, T *ptr) {
printf("customDelete destruct # %x...\n", ptr);
// set this here so we can use it in operator delete above
stupidHack = &alloc;
// this calls the destructor and offsets the pointer to the right spot to be dealloc'd
delete ptr;
printf("OK\n\n");
}
////////////////////////////////////////////////////////////////
struct A {
int a;
A() : a(0) {
printf("A()\n");
}
virtual ~A() {
printf("~A()\n");
}
};
struct B {
int b;
B() : b(0) {
printf("B()\n");
}
virtual ~B() {
printf("~B()\n");
}
};
struct C : A, B {
int c;
C() : c(0) {
printf("C()\n");
}
~C() {
printf("~C()\n");
}
};
////////////////////////////////////////////////////////////////
int main() {
NormalAlloc alloc;
C *c = customNew<C>(alloc);
A *a = c;
B *b = c;
printf("c: %x\n", c);
printf("a: %x\n", a);
printf("b: %x\n", b);
printf("\n");
// now it works
customDelete(alloc, b);
printf("--------------\n\n");
return 0;
}
This isn't a question really more of just a rant as I'm fairly sure that no magic operator or platform independent method to obtain the address exists. At the company where I work we had a library that used custom allocators with the hack above which worked okay until we had to link it statically with another program that needed to replace global new/delete. Our current solution is simply to ban the deleting of an object through a pointer to a base that can't be shown to always have the same address as the most derived object, but this seems a bit unfortunate. "ptr->~T(); free(ptr);" seems to be a common enough pattern and many people seem to think it's equivalent to a delete expression, but it's not. I'm curious if anyone else has encountered this problem and how they managed to solve it.
If p points to an object of polymorphic class type, you can get the address of the most derived object using dynamic_cast<void*>(p). Thus your customDelete2 can be implemented as follows:
template <class T>
void customDelete2(const T *ptr) {
const void* ptr_to_free = dynamic_cast<const void*>(ptr);
ptr->~T();
std::free(const_cast<void*>(ptr_to_free));
}
(Yes, you can dynamically allocate const objects.)
Since this will only compile for a polymorphic class type, you might want to remove the dynamic_cast to a helper function:
template <class T>
const void* get_complete_object_address(const T* p, std::true_type) {
return dynamic_cast<const void*>(p);
}
template <class T>
const void* get_complete_object_address(const T* p, std::false_type) {
return p;
}
template <class T>
void customDelete2(const T *ptr) {
const void* ptr_to_free = get_complete_object_address(
ptr,
std::integral_constant<bool, std::is_polymorphic<T>::value>{}
);
ptr->~T();
free(const_cast<void*>(ptr_to_free));
}
Being on a similar condition of Handling different datatypes in a single structure
I have an implementation using union, but as it takes the highest variable type as its memory size, I end up allocating 64-bit even for an 8-bit variable type. Also I was writing several functions to get/set values on a specific data type.
Example:
class CDataType {
public:
void setTBOOL8(bool src) {
m_uAnyData.um_Bool = src;
}
void setTUINT8(uint8_t src) { //also used for FzrteByte
m_uAnyData.um_UInt8 = src;
}
......
bool getTBOOL8() const {
return (m_uAnyData.um_Bool);
}
uint8_t getTUINT8() const { //also used for FzrteByte
return (m_uAnyData.um_UInt8);
}
.......
private:
union uAnyData {
bool um_Bool;
uint8_t um_Byte;
uint16_t um_Word;
uint32_t um_DWord;
uint64_t um_LWord;
uint8_t um_UInt8;
uint16_t um_UInt16;
uint32_t um_UInt32;
uint64_t um_UInt64;
int8_t um_Int8;
int16_t um_Int16;
int32_t um_Int32;
int64_t um_Int64;
float um_Float;
double um_DFloat;
};
uAnyData m_uAnyData;
};
So I was looking for alternate solution using template
class CDataType {
public:
virtual void get() = 0;
}
template<class T> MyDataType public CDataType{
private:
private T data_;
public:
MyVariantType(T data) {
data_ = data;
}
virtual T get() {
return data_;
}
}
CDataType* var = new MyDataType<int>(100);
var->get();
var = new MyDataType<string>("hello world");
var->get();
I'm not so clear about what you want, but from one of your comments, it seems like this might be useful. (Or at least you might get some ideas from the
code.)
The TPun class is pointed at some location in memory, either with a pointer or reference to an object, and it allows you to read values from arbitrary offsets from the start of the buffer.
Look at the demonstration at the in main() (at the bottom) to see how it can be used.
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
#include <cstddef>
class TPun {
const void* pbuffer;
template <typename IT>
const void* offset_ptr(IT byte_offset) const {
return static_cast<const void*>(static_cast<const char*>(pbuffer) + byte_offset);
}
public:
TPun() : pbuffer(nullptr) {}
template <typename T>
TPun(const T* pbuffer) : pbuffer(static_cast<const void*>(pbuffer)) {}
template <typename T>
TPun(T* pbuffer) : pbuffer(static_cast<const void*>(pbuffer)) {}
template <typename T>
TPun(const T& struct_obj) : pbuffer(static_cast<const void*>(&struct_obj)) {}
template <typename T>
TPun& operator = (const T* pbuf) { pbuffer = static_cast<const void*>(pbuf); return *this; }
template <typename T>
TPun& operator = (T* pbuf) { pbuffer = static_cast<const void*>(pbuf); return *this; }
template <typename T>
TPun& operator = (const T& struct_obj) { pbuffer = static_cast<const void*>(&struct_obj); return *this; }
template <typename T>
struct is_cchar_ptr {
template <typename U> static U& dval();
static int test(char const * const &) { return true; }
template <typename U>
static char test(const U&) { return false; }
static const bool value = sizeof(int) == sizeof(test(dval<T>()));
};
// Returns a reference for non-pointer types (only used for const char*)
template <typename T> struct cptr_pass { static const T& get(const void* p) { return *static_cast<const T*>(p); } };
template <typename T> struct cptr_pass<const T*> { static const T* get(const void* p) { return static_cast<const T*>(p); } };
// at only works for POD types or classes consisting of only POD members
template <typename T, typename IT>
T at(IT byte_offset) const {
if(is_cchar_ptr<T>::value) {
// special handling when T is const char*
// returns a pointer to the buffer instead of a pointer created from buffer data
// using char to access the buffer avoids the strict aliasing rule
return cptr_pass<T>::get(offset_ptr(byte_offset));
}
T value;
std::memcpy(&value, offset_ptr(byte_offset), sizeof(T));
return value;
}
// This version of at() works with any type, but sports Undefined Behavior,
// because it violates the strict aliasing rule.
// This can also cause problems if the offset you give, along with the buffer's
// base address result in a pointer which is not aligned properly for the type.
template <typename T, typename IT>
const T& the_at_of_many_dangers(IT byte_offset) {
return *static_cast<const T&>(offset_ptr(byte_offset));
}
std::ostream& manipulate(std::ostream& is) { return is; }
template<typename M, typename ...Ms>
std::ostream& manipulate(std::ostream& is, const M& manipulator, Ms ...manips) {
return manipulate(is << manipulator, manips...);
}
template <typename T, typename IT, typename ...Ms>
std::string string_at(IT byte_offset, Ms ...manips) {
std::stringstream ss;
manipulate(ss, manips...) << at<T>(byte_offset);
return ss.str();
}
};
void no_warning_strcpy(char* d, const char* s) { while(*(d++) = *(s++)) {} }
struct test_struct {
char text[10];
float number;
int some_int;
double a_double;
test_struct() {
no_warning_strcpy(text, "hi there");
number = 52.25;
some_int = 1000000000;
a_double = 1.2e-14;
}
};
int main() {
using std::cout;
using std::string;
// create a test_struct (values are filled in automatically)
test_struct ts;
// copy ts's memory image to a char buffer
char* memory_buffer = new char[sizeof(ts)];
std::memcpy(memory_buffer, &ts, sizeof(ts));
// Create a TPun object, start off pointing at the char buffer
TPun tpun(memory_buffer);
// send some field values to cout
// the "offsetof" macro (defined in <cstddef>) gives the
// byte offset of a field in a struct
cout << tpun.at<const char*>(offsetof(test_struct, text)) << '\n';
cout << tpun.at<float>(offsetof(test_struct, number)) << '\n';
// format a value into a string. Optionally, add extra ostream manipulator arguments.
// This makes a string with the data from some_int, and formats it as a hex string
string hex_string = tpun.string_at<int>(offsetof(test_struct, some_int), std::hex);
cout << hex_string << '\n';
// I screwed this one up -- I used the text field's data to make an int
// Using the manipulator this way, instead of going directly through
// cout, keeps cout's flags intact
cout << tpun.string_at<int>(offsetof(test_struct, text), std::hex) << '\n';
// change a_double in the original struct
ts.a_double = -1;
// tpun is pointing at the buffer copy, so it shows the original value
cout << tpun.at<double>(offsetof(test_struct, a_double)) << '\n';
// tpun's target can be chaged on-the-fly like this, and it can be set to
// view an object's memory directly, without creating a pointer.
tpun = ts;
// outputs -1
cout << tpun.at<double>(offsetof(test_struct, a_double)) << '\n';
}
This demo prints:
hi there
52.25
3b9aca00
74206968
1.2e-14
-1
I'm totally new here, so I'm not very familiar with style of writing here, so sorry if the question doesn't look like it should.
My question is, how can I create an array of object, but not with default constructors?
If I have something like this:
set<movie> a(3);
set<movie> b(2);
And constructors:
For movie
movie::movie()
{
this->naziv=0;
this->reditelj=0;
this->trajanje=0;
}
movie::movie(char *name, char *red, int len)
{
this->naziv=new char[strlen(name)+1];
strcpy(naziv,name);
this->reditelj=new char[strlen(red)+1];
strcpy(reditelj,red);
this->trajanje=len;
}
And for set:
template<class t>
set<t>::set()
{
this->br=0;
this->niz=0;
}
set<t>::set(int b)
{
this->br=b;
this->niz=new t[br];
}
Answers are great,but on course they teach us some basic stuff about c++,how to create class,template class,I mean,to write programs from the beginning,so for now we don't use that classes and functions that most of you mentioned. The assignment is to write the code this way,so how can I do that?
The assignment is to make a class and a template class,template class is actually an array of objects,so I should make an object,that's an array of objects,and some other functions.
Here's my whole code:
Set.h
#pragma once
#include<iostream>
using namespace std;
template<class t>
class set
{
int br;
t* niz;
public:
set();
set(int b);
~set();
set(set& copy);
int vrati_br_elem()
{
return br;
}
bool pripada(t elem);
set operator*(set& drugi);
friend istream& operator>> <>(istream& ulaz,set<t> &s);
friend ostream& operator<< <>(ostream& izlaz,set<t> &s);
};
template<class t>
set<t>::set()
{
this->br=0;
this->niz=0;
}
template<class t>
set<t>::set(int b)
{
this->br=b;
this->niz=new t[br];
}
template<class t>
set<t>::~set()
{
if(this->niz!=0)
delete [] niz;
}
template<class t>
bool set<t>::pripada(t elem)
{
for(int i=0;i<this->br;i++)
if(this->niz[i]=elem)
return true;
return false;
}
template<class t>
set<t> set<t>::operator *(set<t> &drugi)
{
int broj=0;
set<t> pom((this->br>drugi.br)?this->br:drugi.br);
for(int i=0;i<this->br;i++)
for(int j=0;j<drugi.br;j++)
if(this->niz[i]==drugi.niz[j])
pom.niz[broj++]=this->niz[i];
pom.br=broj;
return pom;
}
template<class t>
istream& operator>>(istream& ulaz,set<t> &s)
{
for(int i=0;i<s.br;i++)
cin>>s.niz[i];
return ulaz;
}
template<class t>
ostream& operator<<(ostream& izlaz,set<t> &s)
{
for(int i=0;i<s.br;i++)
cout<<endl<<s.niz[i]<<endl;
return izlaz;
}
template<class t>
set<t>::set(set<t> ©)
{
this->br=copy.br;
this->niz=new t[br];
for(int i=0;i<this->br;i++)
this->niz[i]=copy.niz[i];
}
movie.h
#include<iostream>
using namespace std;
class movie
{
char* naziv;
char* reditelj;
int trajanje;
public:
movie();
~movie();
movie(movie& copy);
movie(char* name,char* red,int len);
movie& operator=(movie& film);
bool operator==(movie& film);
friend istream& operator>>(istream& ulaz,movie& film);
friend ostream& operator<<(ostream& izlaz,movie& film);
};
movie.cpp
#include"movie.h"
using namespace std;
movie::movie()
{
this->naziv=0;
this->reditelj=0;
this->trajanje=0;
}
movie::~movie()
{
if(naziv!=0&&reditelj!=0)
{
delete [] naziv;
delete [] reditelj;
}
}
movie::movie(movie ©)
{
this->naziv=new char[strlen(copy.naziv)+1];
strcpy(this->naziv,copy.naziv);
this->reditelj=new char[strlen(copy.reditelj)+1];
strcpy(this->reditelj,copy.reditelj);
this->trajanje=copy.trajanje;
}
movie& movie::operator =(movie &film)
{
if(this!=&film)
{
delete [] naziv;
delete [] reditelj;
this->naziv=new char[strlen(film.naziv)+1];
strcpy(this->naziv,film.naziv);
this->reditelj=new char[strlen(film.reditelj)+1];
strcpy(this->reditelj,film.reditelj);
this->trajanje=film.trajanje;
}
return *this;
}
bool movie::operator ==(movie &film)
{
if(!strcmp(this->naziv,film.naziv)&&!strcmp(this->reditelj,film.reditelj)&&this->trajanje==film.trajanje)
return true;
return false;
}
istream& operator>>(istream& ulaz,movie& film)
{
ulaz>>film.naziv>>film.reditelj>>film.trajanje;
return ulaz;
}
ostream& operator<<(ostream& izlaz,movie& film)
{
izlaz<<endl<<film.naziv<<endl<<film.reditelj<<endl<<film.trajanje<<endl;
return izlaz;
}
movie::movie(char *name, char *red, int len)
{
this->naziv=new char[strlen(name)+1];
strcpy(naziv,name);
this->reditelj=new char[strlen(red)+1];
strcpy(reditelj,red);
this->trajanje=len;
}
Don't use built-in arrays, especially if you are new. Built-in arrays are best left to experts and even then they are often best avoided. Instead of using T[n] just use std::vector<T>. This one will start out empty an you can then e.g. push_back() the objects you are interested in.
That said, I don't see where you code excerpt actually has a problem.
You can create an array of objects invoking the constructor directly.
movie objs[2] = {movie(arg1, arg2, arg3), movie(arg1, arg2, arg3)};
The standard way to do this is to use a allocator object, like all the standard containers.
template<class T, class alloc_type =std::allocator<T> >
class set {
typedef alloc_type::pointer pointer; //bring in it's pointer type
alloc_type alloc;
And then, use that for everything:
pointer buffer = alloc.allocate(100);
alloc.construct(buffer+0); //deault construct T
alloc.construct(buffer+1, T()); //construct T from copy
alloc.construct(buffer+2, 17); //construct T from 17
alloc.destroy(buffer+2); //clean up objects
alloc.destroy(buffer+1);
alloc.destroy(buffer+0);
alloc.deallocate(buffer); //clean up buffer
Remember, it's standard to construct from lowest index to highest, and to destroy in the reverse order.
The "correct" way to do this has changed with C++11, but since I use MSVC10, which can't do the correct way, I still use this way.
Basic implementations of each of these functions is rediculously simple, though.
template<class T>
class myallocator {
public:
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef const T* const_pointer;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
myallocator () throw() {}
template <class U> myallocator (const myallocator<U>&) throw() {}
pointer address (reference x) const {return &x;}
const_pointer address (const_reference x) const {return &x;}
size_type max_size() const throw() {return size_type(-1);}
pointer allocate(size_type c,const_pointer h=0){return(T*)new char[sizeof(T)*c];}
void deallocate(pointer ptr, size_type c) {delete [] ptr;}
pointer construct(pointer ptr) {return new(ptr)T;}
template<class U>
pointer construct(pointer ptr, const U& from) {return new(ptr)T(from);}
void destroy(pointer ptr) {ptr->~T();}
};
The two construct members use what is called "placement new" which creates the object in an already existing space. Here, an array of chars.
You are trying to write your own container class, and you should not call it set. I will assume that you are calling it my_set in this answer.
I think this is the line you are interested in. You wish to pass in a default value here:
my_set<t>::my_set(int b, t default_value)
//creates a my_set with 'b' elements, where each element is set to default_value
Is this your goal? If so, it's easier to define niz as a *vector<t> instead of as t*
template<class t>
struct my_set {
int br;
vector<t> *niz;
my_set(int b, const t& default_value);
}
template<class t>
my_set<t>::my_set(int b, const t& default_value) {
//creates a my_set with 'b' elements, where each element is set to 0
this->br=b;
this->niz=new vector<t>(b, default_value);
}
There are other changes you may consider, such as defining niz simply as vector<t>, not as vector<t>*, but I think that's beyond the scope of you original question.
Finally, I have a question of my own for everybody. How can I do an uninitialized array new[] in C++? I'd like to new an array of known size, but with unconstructed data, and then use something like uninitialized_copy to copy data in.
One of the problems in your code is that this will fail if either string is 0
ostream& operator<<(ostream& izlaz,movie& film)
{
izlaz
<< endl << film.naziv // fails if film.naziv == 0
<< endl << film.reditelj // fails if film.reditelj == 0
<< endl << film.trajanje << endl;
return izlaz;
}
That crashes for me. You should not do cout << (char*)0;. It's better to do something like cout << "". You could fix it by changing the constructor of movie:
movie::movie()
{
this->naziv=""; // an empty, non-null, string
this->reditelj=""; // an empty, non-null, string
this->trajanje=0;
}
But a better solution is to stop using char * and use std :: string instead.
The expression new T[n] will always allocate space for n T objects and call the T constructor on each element. Similarly, delete[] niz, will always call the T destructor on each element. It seems that you want to manually control when the T constructor and destructor are called, so rather than using ::operator new[], you could just use ::operator new and its placement syntax.
You want niz to be an array of br T objects. Instead of this:
niz = new T[br];
You can use this:
niz = static_cast<T *>(::operator new(br * (sizeof (T))));
which will allocate space in the heap for br contiguous T objects, but not call the T constructor on any of them. It's basically like using malloc() to allocate space for the T objects.
But, now you have a problem: how do you actually use one of the T objects? Before you can do anything with niz[i], you need to make sure that the ith T object has been constructed. You can use placement new to construct it:
new(niz + i) T();
Notice that niz + i is the pointer to the ith T object. The effect of this statement is that the T constructor is called in place using the space at reinterpret_cast<char *>(niz + i) through reinterpret_cast<char *>(niz + i) + (sizeof (T)).
Now you have another problem: how do you keep track of which T objects have been constructed? You need to know this in order to call the destructor on the ones that have been constructed, or else you might leak memory.
One solution is to use a std::vector<bool> having br bool objects. If the ith bool is true, then you will know that the ith T object was constructed. Otherwise, it needs to be constructed.
In the set<T> destructor, you need to make sure to destroy all T objects that have been constructed. Suppose that the ith T object has been constructed. To call the T destructor on the ith T object, you can use this statement:
(niz + i)->~T();
Putting it all together, you would get something like this:
#include <cstddef>
#include <iostream>
#include <new>
#include <vector>
template <typename T>
class set
{
std::size_t br;
T *niz;
std::vector<bool> constructed;
public:
std::string name;
set()
: br(0), niz(NULL), constructed()
{
}
set(std::size_t br)
: br(br), niz(NULL), constructed(br, false)
{
niz = static_cast<T *>(::operator new(br * (sizeof (T))));
}
void destroy()
{
std::cout << "~set(" << name << ")\n";
if (niz) {
std::vector<bool>::const_iterator begin = constructed.begin(), it, end = constructed.end();
for (it = constructed.begin(); it != end; ++it) {
if (*it) {
(niz + (it - begin))->~T();
}
}
::operator delete(niz);
}
}
~set()
{
destroy();
}
set<T>& operator=(const set<T>& other)
{
if (this != &other) {
destroy();
niz = NULL;
constructed = std::vector<bool>(other.br, false);
br = other.br;
T *tmp = static_cast<T *>(::operator new(other.br * (sizeof (T))));
try {
std::size_t i;
for (i = 0; i < other.br; ++i) {
if (other.constructed[i]) {
new(tmp + i) T(other.niz[i]);
constructed[i] = true;
}
}
} catch (...) {
niz = tmp;
destroy();
throw;
}
niz = tmp;
name = other.name + " (2)";
}
return *this;
}
T& operator[](std::size_t i)
{
if (niz && !constructed[i]) {
new(niz + i) T();
constructed[i] = true;
}
return niz[i];
}
};
struct my_struct
{
char c;
my_struct(char c = 'a')
: c(c)
{
std::cout << "my_struct('" << c << "')\n";
}
~my_struct()
{
std::cout << "~my_struct('" << c << "')\n";
}
};
int main()
{
::set<char> a, a2(3);
a.name = "a";
a2.name = "a2";
{
::set<my_struct> b(3);
b.name = "b";
b[0].c = '1';
b[2].c = '3';
b[1].c = '2';
::set<my_struct> b2(4);
b2.name = "b2";
b = b2; b.name += ", going by the name 'b'";
b[0].c = 'A';
b2[1].c = 'B';
}
}
Note: I do not recommend actually using this code. The point is to learn about the placement new operator and explicitly invoking a destructor through a pointer.
See STL templates vector and set for standard alternatives.