8 Replies - 789 Views - Last Post: 26 July 2012 - 10:18 PM Rate Topic: -----

#1 aaron1178  Icon User is offline

  • Dovakiin, Dragonborn
  • member icon

Reputation: 169
  • View blog
  • Posts: 1,297
  • Joined: 22-October 08

Writing array values to memory, then reloading them

Posted 22 July 2012 - 04:22 AM

Hello everyone,

I am at a crossroads here :(

What my objective is

I've written my own dynamic array. I have a structure named 'header' like so:
struct header
{
	int numBlocks;
	int numBlockTypes;
	GrowableArray<int> blockorder;
	GrowableArray<string> blockTypes;
};


So I have a few ints and a few GrowableArray, one of int and one of string.

I want to create a copy of header called 'head' and assign values. Then I want to write the structure to a binary file. Now, once these values have been written, I want to read them again, but not from memory, meaning I close the program then open it again, so the memory addresses are not the same.

All goes well for the two int variables, but not the arrays. I sort of know why, but I can't bring my head around it to find a solution. Any help?

main.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <Windows.h>
#include <array>
#include "gGrowableArray.h"
using namespace std;

struct header
{
	int numBlocks;
	int numBlockTypes;
	GrowableArray<int> blockorder;
	GrowableArray<string> blockTypes;
};

struct vertex
{
	float x,y,z;
};

struct triangle
{
	int v1,v2,v3;
};

class MeshReaderAlpha
{
public:
	MeshReaderAlpha(){ headerData = new header(); }
	~MeshReaderAlpha(){}

	HRESULT readFile(const char* fileName);
	HRESULT readHeader(ifstream &in);
	header* headerData;
	GrowableArray<vertex> vertices;
	GrowableArray<triangle> triangles;
	int offset;
};

HRESULT MeshReaderAlpha::readHeader(ifstream &in)
{

	return S_OK;
}

HRESULT MeshReaderAlpha::readFile(const char* fileName)
{
	ifstream istream(fileName, ios::binary|ios::in);

	//Read the header
	headerData = new header();
	istream.seekg(0);
	istream.read((char*)headerData, sizeof(*headerData));
	istream.seekg(sizeof(*headerData));
	
	if( headerData->numBlocks < headerData->blockTypes.GetSize() || headerData->numBlocks < headerData->numBlockTypes)
	{
		cout << "Warning: NumBlocks is lower than BlockTypes count!" << endl;
	}

	cout << headerData->blockTypes[0] << endl;//Because of here

	return S_OK;
}

int main() {
	MeshReaderAlpha* mesh = new MeshReaderAlpha();
	mesh->readFile("format.bin");//Error Happens here

	header head;
	head.numBlocks = 4;
	head.numBlockTypes = 4;

	head.blockorder.Add(0);
	head.blockorder.Add(1);
	head.blockorder.Add(2);
	head.blockorder.Add(3);

	head.blockTypes.Add("vertex");
	head.blockTypes.Add("vertex");
	head.blockTypes.Add("vertex");
	head.blockTypes.Add("triangle");

	vertex v1 = {0.0,0.0,0.0};
	vertex v2 = {0.0,1.0,0.0};
	vertex v3 = {1.0,0.0,0.0};
	triangle t1 = {0,1,2};


	ofstream stream("format.bin", ios::binary|ios::out);
	stream.write((const char*)&head,sizeof(head));
	stream.write((const char*)&head.blockTypes,sizeof(head));
	stream.write((const char*)&v1, sizeof(v1));
	stream.write((const char*)&v2, sizeof(v2));
	stream.write((const char*)&v3, sizeof(v3));
	stream.write((const char*)&t1, sizeof(t1));
	cout << "Written to bin file!" << endl;
	stream.close();

	ifstream istream("format.bin", ios::binary|ios::in);
	header* heading = new header();
	istream.seekg(0);
	istream.read((char*)heading, sizeof(*heading));
	istream.seekg(sizeof(*heading));
	if( heading->numBlocks < heading->blockTypes.GetSize() || heading->numBlocks < heading->numBlockTypes)
	{
		cout << "Warning: NumBlocks is lower than BlockTypes count!" << endl;
	}
	GrowableArray<vertex> verts;
	GrowableArray<triangle> tris;
	int offset = istream.tellg();
	for( int i = 0; i < heading->numBlocks; i++ )
	{
		if( i == heading->blockorder[i] )
		{
			if(heading->blockTypes[i] == "vertex")
			{
				vertex* v = new vertex();
				istream.read((char*)v, sizeof(*v));
				verts.Add(*v);
				istream.seekg(offset + sizeof(*v));
				offset = istream.tellg();
			}
			else if(heading->blockTypes[i] == "triangle")
			{
				triangle* t = new triangle();
				istream.read((char*)t, sizeof(*t));
				tris.Add(*t);
				istream.seekg(offset + sizeof(*t));
				offset = istream.tellg();
			}

		}
	}
	
	for( int o = 0; o < verts.GetSize(); o++ )
	{
		cout << "Verticies: " << verts[o].x << "; " << verts[o].y << "; " << verts[o].z << "; " << endl;
	}
	for( int o = 0; o < tris.GetSize(); o++ )
	{
		cout << "Triangles: " << tris[o].v1 << "; " << tris[o].v2 << "; " << tris[o].v3 << "; " << endl;
	}


	system("pause");
	return 0;
}



growableArray.h
#ifndef gArr_H
#define gArr_H
#include "stdafx.h"

/*
*	A growable array that utalises a class
*/
template<typename TYPE> class GrowableArray
{
public:
			GrowableArray()
			{
				aData = NULL; aSize = 0; aMaxSize = 0;
			}
			GrowableArray( const GrowableArray <TYPE>& a)
			{
				for( int i = 0; i < aMaxSize; i++ ) Add( a.aData[i] );
			}
			~GrowableArray()
			{
			}

			const TYPE& operator[](int nIndex) const
			{
				return GetAt(nIndex);
			}

			TYPE& operator[](int nIndex) 
			{
				return GetAt(nIndex);
			}

			GrowableArray& operator=( const GrowableArray <TYPE>& a )
			{
				if( this == &a ) return *this; for( int i = 0; i < a.aSize;
																 i++ ) Add( a.aData[i] ); return *this;
			}

			HRESULT SetSize( int newSize );
			int inline GetSize(void){return aSize; }
			HRESULT Add( const TYPE& value );
			HRESULT Insert( int index, const TYPE& value );
			HRESULT SetAt( int index, const TYPE& value );

			TYPE& GetAt( int index ) const
			{
				assert( index >= 0 && index < aSize );
				return aData[index];
			}

			TYPE* aData;
			int aSize;
			int aMaxSize;

			HRESULT SetSizeInternal( int NewSize );
};

template<typename TYPE> HRESULT GrowableArray <TYPE>::SetSizeInternal( int NewSize )
{
    if( NewSize < 0 || ( NewSize > INT_MAX / sizeof( TYPE ) ) )
    {
        assert( false );
        return E_INVALIDARG;
    }

    if( NewSize == 0 )
    {
        // Shrink to 0 size & cleanup
        if( aData )
        {
            free( aData );
            aData = NULL;
        }
       aMaxSize = 0;
        aSize = 0;
    }
    else if( aData == NULL || NewSize > aMaxSize )
    {
        // Grow array
        int nGrowBy = (aMaxSize == 0 ) ? 16 : aMaxSize;

        // Limit nGrowBy to keep m_nMaxSize less than INT_MAX
        if( ( UINT )aMaxSize + ( UINT )nGrowBy > ( UINT )INT_MAX )
            nGrowBy = INT_MAX - aMaxSize;

        NewSize = __max( NewSize, aMaxSize + nGrowBy );

        // Verify that (nNewMaxSize * sizeof(TYPE)) is not greater than UINT_MAX or the realloc will overrun
        if( sizeof( TYPE ) > UINT_MAX / ( UINT )NewSize )
            return E_INVALIDARG;

        TYPE* pDataNew = ( TYPE* )realloc( aData, NewSize * sizeof( TYPE ) );
        if( pDataNew == NULL )
            return E_OUTOFMEMORY;

        aData = pDataNew;
        aMaxSize = NewSize;
    }

    return S_OK;
}

template<typename TYPE> HRESULT GrowableArray <TYPE>::Add( const TYPE& value )
{
	HRESULT hr;
    if( FAILED( hr = SetSizeInternal( aSize + 1 ) ) )
        return hr;

    assert( aData != NULL );

    // Construct the new element
    ::new ( &aData[aSize] ) TYPE;

    // Assign
    aData[aSize] = value;
    ++aSize;

    return S_OK;
}

#endif



Is This A Good Question/Topic? 0
  • +

Replies To: Writing array values to memory, then reloading them

#2 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5780
  • View blog
  • Posts: 12,594
  • Joined: 16-October 07

Re: Writing array values to memory, then reloading them

Posted 22 July 2012 - 05:54 AM

Um, well, err... sorry, man, this is a train wreck.

Your GrowableArray leaks memory like a sieve. Is there any reason you're avoiding std::vector? You're using std::string, so you're not afraid of STL.

Are those parallel arrays? Why? You have a header, and yet there's data that's dynamically sized? Huh?

Right, to do what you're trying to do, I'd go with something like:
#include <iostream>
#include <vector>
#include <fstream>

struct Vertex { 
	float x,y,z;
	Vertex() : x(0), y(0), z(0) { } 
	Vertex(float _x, float _y, float _z) : x(_x), y(_y), z(_z) { } 
};

struct Triangle { 
	int v1,v2,v3;
	Triangle() : v1(0), v2(0), v3(0) { } 
	Triangle(int _v1, int _v2, int _v3) : v1(_v1), v2(_v2), v3(_v3) { } 
};

struct Mesh {
	std::vector<Vertex> vertices;
	std::vector<Triangle> triangles;
	bool read(const std::string &fileName);
	bool write(const std::string &fileName) const;
	void add(const Vertex &item) { vertices.push_back(item); }
	void add(const Triangle &item) { triangles.push_back(item); }
};


using namespace std;

int main() {
	Mesh mesh;
	

	mesh.add(Vertex(0.0,0.0,0.0));
	mesh.add(Vertex(0.0,1.0,0.0));
	mesh.add(Vertex(1.0,0.0,0.0));
	mesh.add(Triangle(0,1,2));
	mesh.write("format.bin");
	
	return 0;
}

bool Mesh::write(const std::string &fileName) const {
	ofstream stream(fileName.c_str(), ios::binary|ios::out);
	unsigned int vertCount = vertices.size();
	stream.write((const char*)&vertCount,sizeof(vertCount));
	unsigned int triCount = triangles.size();
...



No, wait, it's worse than I thought. But also easier. Perhaps:
struct Triangle { 
	static const int SIDES = 3;
	Vertex v[SIDES];
	Triangle() { }
	Triangle(const Vertex &v1, const Vertex &v2, const Vertex &v3) {
		v[0] = v1; v[1] = v2; v[2] = v3;
	} 
};



Hope this helps.
Was This Post Helpful? 2
  • +
  • -

#3 aaron1178  Icon User is offline

  • Dovakiin, Dragonborn
  • member icon

Reputation: 169
  • View blog
  • Posts: 1,297
  • Joined: 22-October 08

Re: Writing array values to memory, then reloading them

Posted 22 July 2012 - 05:51 PM

baavgai

This really does help :) Thank you. Though this is not what I am trying to achieve. The header structure is like an info structure, it has how many different things are in the file, what type they are ect.

Lets just change a few things shall we.

struct header
{
int numBlocks;
int numBlockTypes;
vector<int> blockorder;
vector<string> blockTypes;
};



So, now I need to write this structures values to a binary file. So it will write numBlocks, numBlockTypes fine, and it will read them fine since they are ints. But the problem is writing the blockorder and blockTypes to the file, so that all values of these vectors are written and can be read back into a new vector of the same type. If you catch my drift.
Was This Post Helpful? 0
  • +
  • -

#4 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3469
  • View blog
  • Posts: 10,689
  • Joined: 05-May 12

Re: Writing array values to memory, then reloading them

Posted 22 July 2012 - 06:03 PM

The blockOrder you can write out and read back in using a single write or read operation by using the vector<>.data() method. Remember to allocate space first before reading in. :lol:

I think that you are forced to iterate over blockTypes and write them out one string at a time. For reading back in, again, you'll have to read them in one string at time.
Was This Post Helpful? 0
  • +
  • -

#5 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5780
  • View blog
  • Posts: 12,594
  • Joined: 16-October 07

Re: Writing array values to memory, then reloading them

Posted 23 July 2012 - 06:34 AM

The problem is, you want to write binary and string is a complex type. It doesn't really lend itself to a binary file. In your new struct with vector, the numBlocks and numBlockTypes is pointless, since vector knows that.

I'd start with something like:
struct Header {
	// these are pointless
	// vector has a size
	// int numBlocks; 
	// int numBlockTypes;
	std::vector<int> blockorder;
	std::vector<std::string> blockTypes;
};

void save(int, std::ostream &);
void save(const std::string &, std::ostream &);
void save(const std::vector<int> &, std::ostream &);
void save(const std::vector<std::string> &, std::ostream &);
void save(const Header &, std::ostream &);

void load(int &, std::istream &);
void load(std::string &, std::istream &);
void load(std::vector<int> &, std::istream &);
void load(std::vector<std::string> &, std::istream &);
void load(Header &, std::istream &);

void print(const Header &);



Saving a string would be the most tricky. Perhaps something like:
// the int implementation, useful for the rest
void save(int n, std::ostream &os) { os.write((const char *)&n, sizeof(n)); }

void save(std::string> &s, std::ostream &os) {
	// save the size
	save(s.size(), os);
	// now just save the characters
	os.write((const char *)&s.c_str(), s.size());
}


Was This Post Helpful? 0
  • +
  • -

#6 aaron1178  Icon User is offline

  • Dovakiin, Dragonborn
  • member icon

Reputation: 169
  • View blog
  • Posts: 1,297
  • Joined: 22-October 08

Re: Writing array values to memory, then reloading them

Posted 25 July 2012 - 11:31 PM

Thank you Skydiver and baavgai.

You have both helped me figure out what I have been trying to for months. I am still performing test and little edits to make things easier. I will post the code of a draft once I prototype the code required and just think of things.

Note* I have also been studying NifLib, which is what I am aiming at, though smaller. I am understanding it a lot more thanks to your last post baavgai.

It also helps to keep a hex editor beside you while doing this kind of stuff :)

This post has been edited by aaron1178: 25 July 2012 - 11:32 PM

Was This Post Helpful? 0
  • +
  • -

#7 aaron1178  Icon User is offline

  • Dovakiin, Dragonborn
  • member icon

Reputation: 169
  • View blog
  • Posts: 1,297
  • Joined: 22-October 08

Re: Writing array values to memory, then reloading them

Posted 26 July 2012 - 04:22 AM

Well, I've been prototyping and came up with some code. I have no errors... yet, but that is not why I am back before I have finished. The reason being, I created a method to write the strings to the file right, now all goes smoothly. Except I write 'vertex' 6 bytes long, and for some reason it only stores 4 bytes in the file?

Posted Image

BinaryReader.h
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
using namespace std;

struct vertex
{
	float x, y, z;
};

struct triangle
{
	vertex v1, v2, v3;
};

struct header
{
	int numObjects;
	std::vector<std::string> blockTypes;
	std::vector<int> blockIndexes;
	std::vector<int> blockSizes;
};

#ifndef BINARY_READER
#define BINARY_READER

/*
* This file contains a prototype custom 
* mesh format reader. This is purley experimental
* and should not be considered complete.
*/

#pragma region Forward R/W Method Declarations

//<summary>
//These are the declaration methods for 
//binary reading and writing.
//All reading methods will return values.
//</summary>

//Forward delc (write int type)
void WriteInt(const int, ostream &);

//Forward delc (write std::string)
void WriteString(const std::string, ostream &);

//Forward delc (write struct header)
void WriteHeader(header &, ostream &);

//Forward delc (read int type)
int ReadInt( ifstream &);

//Forward delc (read std::string type)
std::string ReadString( ifstream &);

#pragma endregion 

#pragma region R/W Methods

//WriteInt(const int, ostream &)
//Writes an int to the out stream
void WriteInt(const int value, ostream &out)
{
	//Writes data at values address(&) to out stream.
	out.write((const char*)&value, sizeof(value));
}

//WriteString(const std::string, ostream &)
//Writes a std::string to the out stream
void WriteString(const std::string str, ostream &out)
{
	//First we write the strings size to the out stream
	WriteInt(str.size(), out);

	//Now we write the dat at strs address(&) to out stream.
	out.write((const char*)str.c_str(), sizeof(str.size()));
}

//WriteHeader(const header, ostream &)
//Writes a struct header to the out stream
void WriteHeader( header &hd, ostream &out)
{
	//Write NumObjects
	WriteInt(hd.numObjects, out);
	
	//Write Block Indexes
	std::vector<int>::pointer bIndexsP;
	bIndexsP = hd.blockIndexes.data();

	//Write Size of Block Indexes
	WriteInt(hd.blockIndexes.size(), out);

	for(size_t i = hd.blockIndexes.size(); 0 < i; --i, bIndexsP++)
	{
		WriteInt(*bIndexsP, out);
	}

	//Write Block Sizes
	std::vector<int>::pointer bSizesP;
	bSizesP = hd.blockSizes.data();

	//Write size foe Block Sizes
	WriteInt(hd.blockSizes.size(), out);

	for(size_t i = hd.blockSizes.size(); 0 < i; --i, bSizesP++)
	{
		WriteInt(*bSizesP, out);
	}

	//Write blockTypes

	std::vector<std::string>::pointer bTypesP;
	bTypesP = hd.blockTypes.data();

	for(size_t i = hd.blockTypes.size(); 0 < i; --i, bTypesP++)
	{
		WriteString(*bTypesP, out);
	}

}

//ReadInt(ifstream &in)
//Reads a int from the in stream
int ReadInt( ifstream &in )
{
	int tmp = 0;
	in.read((char*)&tmp, 4);
	return tmp;
}

//ReadString(ifstream &in)
//Reads a std::string from the in stream
std::string ReadString( ifstream &in )
{
	std::string tmp;
	int tSize = ReadInt(in);
	tmp.resize(tSize);
	in.read((char*)&tmp[0], tSize);
	return tmp;
}

#pragma endregion

#endif



Main.h
//0000000000000000000//
//Vector Test Project//
//0000000000000000000//
#include "binary.h"

int main()
{
	header head;
	head.numObjects = 1;
	
	head.blockTypes.push_back("vertex");
	head.blockIndexes.push_back(1);
	head.blockSizes.push_back(sizeof(vertex));
	vertex v1;
	v1.x = 1.0, v1.y = 2.0, v1.z = 3.0;



	cout << "Binary Reader tests" << endl;

	ofstream out("index.bin", ios::binary||ios::in);

	if(out.fail())
	{
		cout << "Failed opening file!" << endl;
	}

	WriteHeader(head, out);

	cout << "Written values to file" << endl;

	cout << "Reading values from file" << endl;

	ifstream in("index.bin", ios::binary||ios::in);
//	int read = ReadInt(in);
//	std::string str = ReadString(in);
//	cout << read << endl << str << endl;

	system("pause");
}
/*	for(size_t e = h.BlockTypes.size(); 0 < e; --e, bTPointer++)
	{
		save(*bTPointer, out);
	}

	cout << "Saved to file !!!!" << endl;

	ifstream in("index.bin", ios::binary||ios::in);
	int e = 0;

	int size = 0;
	load(size, in);

	for( int i = 0; i < size; i++ )
	{
		load(e, in);
		cout << e << endl;
	}

	*/



Any Ideas?

This post has been edited by aaron1178: 26 July 2012 - 04:27 AM

Was This Post Helpful? 0
  • +
  • -

#8 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5780
  • View blog
  • Posts: 12,594
  • Joined: 16-October 07

Re: Writing array values to memory, then reloading them

Posted 26 July 2012 - 05:02 AM

Heh. Did this guy even compile? Wouldn't for me. I'll admit, you got me with this one...
ofstream out("index.bin", ios::binary || ios::in);



Two things you want to ask yourself. First, why do you need ios::in for an ostream. Second, what exactly does "||" mean? For the second one, if you wanted to be explicit, try:
ifstream in("index.bin", ios::binary | ios::in);



By writing something like WriteIntVector, your code can be shortened:
void WriteIntVector(std::vector<int> &v, ostream &out) {
	std::vector<int>::pointer p = v.data();
	for (size_t i = v.size(); 0 < i; --i, p++) {
		WriteInt(*p, out);
	}
}

void WriteHeader(header &hd, ostream &out) {
	WriteInt(hd.numObjects, out);
	WriteInt(hd.blockIndexes.size(), out);
	WriteIntVector(hd.blockIndexes, out);
	WriteInt(hd.blockSizes.size(), out);
	WriteIntVector(hd.blockSizes, out);

	std::vector<std::string>::pointer bTypesP = hd.blockTypes.data();
	for (size_t i = hd.blockTypes.size(); 0 < i; --i, bTypesP++) {
		WriteString(*bTypesP, out);
	}

}



Also, that's a rather quirky way to iterate over a vector. I had to read it a few times to make sure there wasn't a bug in it. I'd go with the more traditional:
void WriteIntVector(const std::vector<int> &v, ostream &out) {
	std::vector<int>::const_iterator it = v.begin();
	for (; it==v.end(); ++it) {
		WriteInt(*it, out);
	}
}



You're back to the generic object and parallel array design. I'd really try to stick them together with a single storage struct.
Was This Post Helpful? 0
  • +
  • -

#9 aaron1178  Icon User is offline

  • Dovakiin, Dragonborn
  • member icon

Reputation: 169
  • View blog
  • Posts: 1,297
  • Joined: 22-October 08

Re: Writing array values to memory, then reloading them

Posted 26 July 2012 - 10:18 PM

View Postbaavgai, on 26 July 2012 - 11:02 PM, said:

You're back to the generic object and parallel array design. I'd really try to stick them together with a single storage struct.


By that, what are you referring to? I mean, what do you mean?

I will fix up my code :)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1