• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
  • entry
    1
  • comments
    0
  • views
    3959

Subverting the C++ type system - Bad Style?

Sign in to follow this  
Followers 0
mrbastard

360 views

I've been using this template iterator for a while now, and find it really useful.

(I suggest skipping to the tests to get an idea of what it looks like in practice)

The inspiration was to help avoid rewriting code that operates on vertices again and again for different game engines / libs with different vertex types. It works nicely on pixels too and allows me to write generic image processing functions in an STL-ish style.

The first use was to rewrite some basic heightmapping stuff as a template function that operated on iterators conforming to STL's RandAccessIterator concept. I then used the below iterator to allow me to generate vertices directly into (almost) anywhere I want. It works well for streaming directly into a mapped VBO for instance, saving creation and copying of a system memory copy. It also means I'll never have to write it again - it will work with any Vertex, Vec3, plain array[] whatever structure. I've used it with my own engine and Irrlicht so far.

It may seem familiar to anyone who has used the gl*Pointer family of functions.

Thing is, it deliberately subverts the C++ type system.

Bad style?


The iterator itself:
//InterleavedArrayIterator.h
//Provides iterator classes for accesing arbitrary arrays of interleaved components
//Primary uses are to allow STL-like generalised algorithms to iterate over any data
//that can be used as OpenGL vertex attribute data, and to allow image processing algorithms to be written
//without relying on a specific format/layout in memory .
//That being the case, it will operate on any sequential block of memory (e.g. plain arrays, std::vector)
//Given a compound class it provides an interface to iterate over some or all of the components as
//if they were a plain array of the component type. The pointer to member operator is also implemented,
//so the dereferenceable component type can itself be a compound type.

//see the tests for examples

#include

//Models the stl concept of Random Access Iterator
//dereference is not range checked, for speed

template
class InterleavedArrayIterator : public std::iterator
//note: implicit distance type is platform int, at least on ms compiler
//ms stl and alloc stuff assumes that if you can alloc it, you can index it with an int. Fair enough.
//I use distance_type instead of int, so hopefully the compiler and stl implementation should always sort it
//Still, it's worth checking the platform distance_type before using on 64bit platforms where mem can be > 2GB
{
distance_type m_stridebytes; //full element size in bytes
distance_type m_size; //number of adjacent dereferenceable elements
const void * m_pointer;

distance_type full_pos;
distance_type derefable_pos;

public:
//copy constructor
InterleavedArrayIterator(const InterleavedArrayIterator& r)
{
//copy vals
m_size = r.m_size;
m_stridebytes = r.m_stridebytes;
m_pointer = r.m_pointer;
full_pos = r.full_pos;
derefable_pos=r.derefable_pos;
}
//same params as gl*Pointer
InterleavedArrayIterator(const unsigned int& size, const unsigned int& stride, const void* ptr)
{
m_size =size; //change to use param list for speed
m_stridebytes = stride;
m_pointer = ptr;
full_pos=0;
derefable_pos=0;
}

///assignment operator
InterleavedArrayIterator&
operator=(const InterleavedArrayIterator& r)
{
if(this == &r)
{
return *this;
}
//copy vals
m_size = r.m_size;
m_stridebytes = r.m_stridebytes;
m_pointer = r.m_pointer;
full_pos = r.full_pos;
derefable_pos=r.derefable_pos;
return *this;
}
///preincrement operator; to model ForwardIterator; returns incremented value
const InterleavedArrayIterator&
operator++()
{
derefable_pos++;
if(derefable_pos >= m_size)
{
derefable_pos -=m_size;
full_pos++;
}
return *this;
}
///postincrement operator; to model ForwardIterator, returns the value before increment
const InterleavedArrayIterator
operator++(int a)
{
InterleavedArrayIteratorbefore(*this);
++(*this);
return before;
}

///predecrement operator; to model Bidirectional Iterator; returns incremented value
const InterleavedArrayIterator&
operator--()
{
derefable_pos--;
if(derefable_pos < 0)
{
derefable_pos += m_size;
full_pos--;
}
return *this;
}

///postdecrement operator; to model Bidirectional Iterator; returns the value before increment
const InterleavedArrayIterator
operator--(int a)
{
InterleavedArrayIteratorbefore(*this);
--(*this);
return before;
}

///iterator addition & ; to model RandomAccessIterator
InterleavedArrayIterator&
operator+=(const distance_type& right)
{
distance_type m = right%m_size;
derefable_pos += m;
full_pos += (right-m)/m_size;
if(derefable_pos >= m_size)
{
full_pos++;
derefable_pos -= m_size;
}
return *this;
}

///iterator addition; to model RandomAccessIterator
//make this friend func? makes more sense
const InterleavedArrayIterator
operator+ (const distance_type& right) const
{
InterleavedArrayIteratorr(*this);
r+=right;
return r;
}

///iterator subtraction & ; to model RandomAccessIterator
InterleavedArrayIterator&
operator-=(const distance_type& right)
{
distance_type m = right%m_size;
derefable_pos -= m;
full_pos -= (right-m)/m_size;
if(derefable_pos < 0)
{
full_pos--;
derefable_pos += m_size;
}
return *this;
}

///iterator subtraction ; to model RandomAccessIterator
const InterleavedArrayIterator
operator- (const distance_type& right) const
{
InterleavedArrayIteratorr(*this);
r-=right;
return r;
}

///difference ; to model RandomAccessIterator
const difference_type operator-(const InterleavedArrayIterator& right) const
{
return ((full_pos - right.full_pos)*m_size) + (derefable_pos - right.derefable_pos);
}

///element access operator ; to model RandomAccessIterator
dereftype& operator[](const distance_type& x) const
{
InterleavedArrayIteratorr(*this);
r+=x;
return * r;
}

///operator lessthan; to model LessThan Comparable for RandomAccessIterator
bool operator<(const InterleavedArrayIterator&right)const
{
return ((full_pos*m_size)+derefable_pos)
< ( (right.full_pos*right.m_size)+right.derefable_pos);
}

///dereference ; to model TrivialIterator etc
dereftype& operator*() const
{
unsigned char * ptr = (unsigned char*)m_pointer + (full_pos*m_stridebytes) + (derefable_pos*sizeof(dereftype));
return *(dereftype*)ptr;
}

///
dereftype* operator->() const
{
unsigned char * ptr = (unsigned char*)m_pointer + (full_pos*m_stridebytes) + (derefable_pos*sizeof(dereftype));
return (dereftype*)ptr;
}

///comparison ; to model Equality Comparable
bool operator==(const InterleavedArrayIterator&right)const
{
if(right.derefable_pos != derefable_pos)
{
return false;
}
if(right.full_pos != full_pos)
{
return false;
}
if(right.m_pointer != m_pointer)
{
return false;
}
if(right.m_stridebytes != m_stridebytes)
{
return false;
}
if(right.m_size != m_size)
{
return false;
}
return true;

}
///comparison ; to model Equality Comparable
bool operator!=(const InterleavedArrayIterator&right)
{
return ! (*this == right);
}
};








a handy proxy for safely(?) creating IAIs
#ifndef INTERLEAVEDARRAYPROXY_H
#define INTERLEAVEDARRAYPROXY_H

#include "InterleavedArrayIterator.h"

//!A Proxy for creating InterleavedArrayIterators from specific containers/arrays
template
class InterleavedArrayProxy
{
typedef InterleavedArrayIterator iterator;

//!full element size in bytes
unsigned int m_stridebytes;
//!number of adjacent dereferenceable elements
unsigned int m_size;
//!base pointer
const void * m_pointer;
//!total number of elements
/*!note: not full_elements but number of m_size clusters*/
unsigned int m_numelements;

public:
InterleavedArrayProxy(const unsigned int& size, const unsigned int& stride, const void* ptr, const unsigned int& numelements)
{
m_size = size;
m_stridebytes = stride;
m_pointer = ptr;
m_numelements = numelements;
}
InterleavedArrayIterator begin() const
{
return iterator(m_size,m_stridebytes,m_pointer);
}

InterleavedArrayIterator end() const
{
iterator i(begin());
i+=(m_numelements*m_size);
return i;
}
};

#endif //defined INTERLEAVEDARRAYPROXY_H







some boost unit tests for completeness sake
// InterleavedArrayIteratorTestMain.cpp : Defines the entry point for the console application.
//

#define BOOST_AUTO_TEST_MAIN
#include

#include "InterleavedArrayProxy.h"

#include
#include

struct compound
{
float x,y;
};

struct testvert
{
float x,y,z;
float nx,ny,nz;
compound tex_coords;
};



//should really just assign the verts once as a test fixture - requires less automatic use of boost.test

BOOST_AUTO_UNIT_TEST( filltest )
{
/*
tests the following:

IAP:
constructor
begin
end
IAI:
copy constructor
constructor
preincrement
iterator addition
dereference*
comparison

*/

testvert v[100];
InterleavedArrayProxy<float>proxy(3,sizeof(testvert),v,100);
std::fill(proxy.begin(),proxy.end(),1.0f);
bool failed = false;
for(unsigned int count=0; count<100; count++)
{
if ( (v[count].x != 1.0) || (v[count].y != 1.0) || (v[count].z != 1.0) )
{
failed = true;
}
}
BOOST_CHECK(!failed);

}

BOOST_AUTO_UNIT_TEST(testSTLfor_each)
{

}

/*
The following are assumed to be trivial and are not tested:
operator==
operator!=
construction
destruction

*/


BOOST_AUTO_UNIT_TEST(testPointerDereferenceOperator)
{
testvert v[100];
InterleavedArrayProxyproxy(1,sizeof(testvert),&v[0].tex_coords,100);
InterleavedArrayIteratori = proxy.begin();
unsigned int t = 0;
for(;i!=proxy.end();++i)
{
i->x=(float)t;
i->y=(float)t;
t++;
}
bool failed = false;
t=0;
for (i=proxy.begin();i!=proxy.end();++i)
{
if ((i->x)!=(float)t || (i->y)!=(float)t)
{
failed = true;
}
t++;
}
BOOST_CHECK(!failed);
}

BOOST_AUTO_UNIT_TEST(testElementAccess)
{
testvert v[100];
InterleavedArrayProxy<float>proxy(3,sizeof(testvert),v,100);
InterleavedArrayIterator<float>i = proxy.begin();
unsigned int t = 0;
for(;i!=proxy.end();++i)
{
*i=(float)t;
t++;
}
bool failed = false;
i=proxy.begin();
for (unsigned int count=0;count<300;count++)
{
if (i[count]!=count)
{
failed = true;
}
i[count]=666.0f;
}
BOOST_CHECK(!failed);

for (unsigned int count=0;count<300;count++)
{
if (i[count]!=666.0f)
{
failed = true;
}
}
BOOST_CHECK(!failed);
}

BOOST_AUTO_UNIT_TEST (testAssignment)
{
//assumes above tests have been passed
InterleavedArrayProxy<float>proxy(3,sizeof(testvert),NULL,100);
InterleavedArrayIterator<float> i = proxy.begin();
BOOST_CHECK(i == proxy.begin());

}

BOOST_AUTO_UNIT_TEST(testPlusEqualsDistanceType)
{
//assumes operator++ works
InterleavedArrayProxy<float>proxy(3,sizeof(testvert),NULL,100);
InterleavedArrayIterator<float> i = proxy.begin();
InterleavedArrayIterator<float> j = proxy.begin();
i+=1;
j++;
BOOST_CHECK(i==j);
}

BOOST_AUTO_UNIT_TEST(testMinusEqualsDistanceType)
{
//assumes operator-- works
InterleavedArrayProxy<float>proxy(3,sizeof(testvert),NULL,100);
InterleavedArrayIterator<float> i = proxy.begin();
InterleavedArrayIterator<float> j = proxy.begin();
i-=1;
j--;
BOOST_CHECK(i==j);
}

BOOST_AUTO_UNIT_TEST(testPostIncrement)
{
//assumes above tests have been passed
//assumes operator+(distance_type) works
InterleavedArrayProxy<float>proxy(3,sizeof(testvert),NULL,100);
InterleavedArrayIterator<float> i = proxy.begin();
BOOST_CHECK(i++ == proxy.begin()); //check the value stays the same inside the statement
BOOST_CHECK(i == proxy.begin()+1); //check the value has been incremented really

}

BOOST_AUTO_UNIT_TEST(testPreDecrement)
{
//test assumes IAP works
//test assumes IAI::operator==(IAI), IAI::operator= and IAI::preinc are working
InterleavedArrayProxy<float>proxy(3,sizeof(testvert),NULL,100);
InterleavedArrayIterator<float> i = proxy.begin();
++i;
--i;
BOOST_CHECK(i == proxy.begin()); //check the value has been incremented really
}

BOOST_AUTO_UNIT_TEST(testSubtractDistanceType)
{
//test assumes IAP works
//test assumes IAI::operator==(IAI), IAI::operator= and IAI::preinc are working
//internally assumes operator-=(distance_type) works
testvert v[100];
InterleavedArrayProxy<float>proxy(3,sizeof(testvert),v,100);
InterleavedArrayIterator<float> a = proxy.begin();
++a;
BOOST_CHECK(proxy.begin() == (a-1));

}

BOOST_AUTO_UNIT_TEST(testPostDecrement)
{
//test assumes IAP works,
//test assumes IAI::operator=, IAI::operator== and IAI::operator-(distance_type) work
//internally assumes predecrement works
InterleavedArrayProxy<float>proxy(3,sizeof(testvert),NULL,100);
InterleavedArrayIterator<float> i = proxy.begin();
BOOST_CHECK(i-- == proxy.begin()); //check the value stays the same inside the statement
BOOST_CHECK(i == proxy.begin()-1); //check the value has been decremented really
}

BOOST_AUTO_UNIT_TEST(testDifference)
{
//test assumes IAP works,
//test assumes IAI::operator=, IAI::operator== work
InterleavedArrayProxy<float>proxy(3,sizeof(testvert),NULL,100);
InterleavedArrayIterator<float> i = proxy.begin();
++i;
int difference = (int)(i - proxy.begin());
BOOST_CHECK(difference==1);
difference = (int)(proxy.begin() -i);
BOOST_CHECK(difference==-1);
}

// _____________________________________







0
Sign in to follow this  
Followers 0


0 Comments


There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now