Jump to content

  • Log In with Google      Sign In   
  • Create Account

TheComet

Member Since 02 Oct 2013
Online Last Active Today, 10:17 AM

Topics I've Started

Generic events - Enforcing the correct function signatures.

18 January 2015 - 02:43 PM

I began development on a game written in C89 about a month ago. The design of this game is entirely centred around a plugin system which basically consists of a module loader, a service directory, and an events system.

The events system is horribly prone to human error and this is what I want to get some input on.

Consider the host application and two plugins, pluginA and pluginB. pluginA registers an event "foo" to the host application, and pluginB listens to that event. The flow of information, thus, looks like this:

         host app
      event dispatcher
        ^          \
       /            \
      /              v
   pluginA         pluginB
FIRE_EVENT(foo)  EVENT_LISTENER(on_foo)

The event system is implemented as follows:

/* generic function, any amount of arguments can be passed to a call of this function type (only works in C, not C++) */
typedef void (*event_listener_callback)();

struct event_t
{
    char* name;
    struct vector_t listeners; /* a vector of event_listener_t objects */
};

struct event_listener_t
{
    char* listener_name;
    event_listener_callback exec;
};

#define EVENT_H(event_name) extern struct event_t* event_name;
#define EVENT_C(event_name) struct event_t* event_name = NULL;

#define EVENT_FIRE0(event_name) \
    VECTOR_FOR_EACH((event_name)->listeners, struct event_listener_t, listener) \
    { \
        listener->exec(event_name); \
    }

#define EVENT_LISTENER0(name) \
    void name(const struct event_t* evt)

Note that there are macros for EVENT_FIRE1, EVENT_FIRE2, etc. depending on how many arguments the event wishes to send.

 

From pluginA's perspective, this is how it would register an event:

EVENT_C(evt_foo)

void pluginA_init(void)
{
    evt_foo = event_create("foo"); /* this returns a new event_t object */
}

From pluginB's perspective, this is how it would listen to the event "foo":

void register_events(void)
{
    event_register_listener("foo", on_foo); /* this adds the function "on_foo" to the corresponding event's listeners vector */
}

EVENT_LISTENER0(on_foo)
{
    puts("event foo received!");
}

The Problem

 

The issue with this system is that the receiving function "on_foo" could have any function signature, and it would compile anyway. Similarly, EVENT_FIRE could callback any number of arguments and yet it would compile anyway.

 

Furthermore, from pluginB's perspective, it's very hard to figure out what each event's callback function signature should be. The only place where it's defined is EVENT_FIRE0().

 

Is there any better way to implement this? Is there any way to enforce the programmer to match all function signatures correctly? The fact that the callback is delegated using a generic function probably makes this impossible, so I'm wondering if there's a better way to implement such a system?

 

[EDIT] How do I add the "C" prefix to my thread?


Very simple linking issue

10 January 2015 - 10:56 PM

Can someone tell me why GCC fails to link? It's 6 lines of code and two CMakeLists.txt files. If someone experienced with CMake could clone this and explain what the big issue is, I'd be glad.
https://github.com/TheComet93/cmake-sucks

I've wasted like an entire hour on this crap and it makes no sense. target_link_libraries() is being called in tests/CMakeLists.txt. The symbols exist (verified with readelf). Nothing appears wrong.

It's 6 lines of code yet it's so complicated.


Linking shared libs against a single static lib

19 December 2014 - 02:51 PM

I'm writing a program that dynamically loads shared libraries (plugins) in plain C. I have also written a utility library containing linked lists, vectors, string functions, etc. The utility library is compiled statically.

 

Here's the problem: When loading two or more of the plugins, I get the following warnings (only showing one of the many warnings, as they're all the same just with different function names):

objc[25253]: Class list_create is implemented in both build/bin/plugins/plugin_renderer_gl-0-0-1.dylib
and build/bin/plugins/plugin_input_glfw-0-0-1.dylib.
One of the two will be used. Which one is undefined.

Note that in GCC by default all symbols are exported.

 

This presents an issue, because there is some static data allocated in the utility library and if it is not defined which instance of the static data is being accessed, then I have a problem.

 

I don't want to turn the utility library into a shared library if possible.

 

Any ideas what I can do?


O(pow(N,12))

22 October 2014 - 04:01 PM

#include "main.h"
#include "renderSystem.h"
#include "largeAsteroid.h"

// To future Karl, or anybody who may have the joy of upkeeping this code.
// I am truly sorry.
#define ASTEROID_SIZE 50

AsteroidVoxel::AsteroidVoxel() :
	mExist( false )
{
}

AsteroidVoxelVertex::AsteroidVoxelVertex()
{
	mExist = true;
}

void AsteroidVoxelVertex::setVertex( u32 vertex )
{
	mVertex = vertex;
}

AsteroidVoxelVertex::~AsteroidVoxelVertex()
{
}

AsteroidData::AsteroidData()
{

	//This part is a pain, but initialize the mVoxelData array
	for( s32 x = 0; x < ASTEROID_SIZE; x++ )
	{
		for( s32 y = 0; y < ASTEROID_SIZE; y++ )
		{
			for( s32 z = 0; z < ASTEROID_SIZE; z++ )
			{
				mVoxelData[x][y][z].mExist = false;
			}
		}
	}

}

AsteroidData::~AsteroidData()
{

}

void AsteroidData::genTestShape()
{
	for( s32 x = 2; x < ASTEROID_SIZE - 2 ; x++ )
		for( s32 y = 2; y < ASTEROID_SIZE - 2; y++ )
			for( s32 z = 2; z < ASTEROID_SIZE - 2; z++ )
				mVoxelData[x][y][z].mExist = true;
}

void AsteroidData::generateBaseShape()
{
	// Set up random number generator.
	mt19937 generator;
	generator.seed( gRenderSystem->getOgreRoot()->getTimer()->getMilliseconds() * rand() % 100000 );

	Vec3 center( ASTEROID_SIZE / 2.0f , ASTEROID_SIZE / 2.0f, ASTEROID_SIZE / 2.0f );
	for( s32 x = 0; x < ASTEROID_SIZE; x++ )
		for( s32 y = 0; y < ASTEROID_SIZE; y++ )
			for( s32 z = 0; z < ASTEROID_SIZE; z++ )
				if( ( Vec3( x, y, z ) - center ).LengthSq() < ( ( ASTEROID_SIZE / 2.0f ) * ( ASTEROID_SIZE / 2.0f ) ) )
					mVoxelData[x][y][z].mExist = true;

	//Add craters.
	uniform_int_distribution<s32> craterImpacts( 2, 10 );
	uniform_real_distribution<f32> craterPosition( 0.0f, ASTEROID_SIZE );
	uniform_real_distribution<f32> craterRadius( ASTEROID_SIZE / 8.0f, ASTEROID_SIZE / 4.0f );

	u32 noCraterImpacts = craterImpacts( generator );

	for( u32 i = 0; i < noCraterImpacts; ++i )
	{
		Vec3 origin( craterPosition( generator ), craterPosition( generator ), craterPosition( generator ) );
		f32 lCraterRadius = craterRadius( generator );

		for( u32 x = 0; x < ASTEROID_SIZE; x++ )
			for( u32 y = 0; y < ASTEROID_SIZE; y++ )
				for( u32 z = 0; z < ASTEROID_SIZE; z++ )
					if( ( Vec3( x, y, z ) - origin ).LengthSq() < lCraterRadius * lCraterRadius ) {}
		//mVoxelData[x][y][z].mExist = false;
	}

}

void AsteroidData::generateIndexVertexBuffers()
{
	// Eliminate non-visible voxels
	// Check each of the 6 sides.
	vector<AsteroidVoxelVertex*> mVisibleVoxels;
	AsteroidVoxelVertex* mVisVoxelStructure[ASTEROID_SIZE][ASTEROID_SIZE][ASTEROID_SIZE];

	u32 vertexCount = 0;

	for( s32 x = 0; x < ASTEROID_SIZE; x++ )
	{
		for( s32 y = 0; y < ASTEROID_SIZE; y++ )
		{
			for( s32 z = 0; z < ASTEROID_SIZE; z++ )
			{

				bool canBeSeen = 0;

				if( x == 0 || x == ASTEROID_SIZE - 1 || y == 0 || y == ASTEROID_SIZE - 1 || z == 0 || z == ASTEROID_SIZE - 1 )
					if( mVoxelData[x][y][z].mExist == true )
						canBeSeen = true;

				if( x > 0 )
					if( mVoxelData[x - 1][y][z].mExist == false )
						canBeSeen = true;

				if( y > 0 )
					if( mVoxelData[x][y - 1][z].mExist == false )
						canBeSeen = true;

				if( z > 0 )
					if( mVoxelData[x][y][z - 1].mExist == false )
						canBeSeen = true;

				if( x < ASTEROID_SIZE - 1 )
					if( mVoxelData[x + 1][y][z].mExist == false )
						canBeSeen = true;

				if( y < ASTEROID_SIZE - 1 )
					if( mVoxelData[x][y + 1][z].mExist == false )
						canBeSeen = true;

				if( z < ASTEROID_SIZE - 1 )
					if( mVoxelData[x][y][z + 1].mExist == false )
						canBeSeen = true;

				if( canBeSeen )
				{
					if( mVoxelData[x][y][z].mExist == true )
					{
						// Set vertex
						AsteroidVoxelVertex* vertex = new AsteroidVoxelVertex;
						vertex->setVertex( vertexCount );

						//Add to data structures
						mVisibleVoxels.push_back( vertex );
						mVisVoxelStructure[x][y][z] = vertex;

						// Add to vertex list
						mVertex.push_back( f32( x ) * 100.0 );
						mVertex.push_back( f32( y ) * 100.0 );
						mVertex.push_back( f32( z ) * 100.0 );

						vertexCount++;
					}
					else
					{
						AsteroidVoxelVertex* vertex = new AsteroidVoxelVertex;
						vertex->mExist = false;

						mVisibleVoxels.push_back( vertex );
						mVisVoxelStructure[x][y][z] = vertex;
					}
				}
				else
				{
					AsteroidVoxelVertex* vertex = new AsteroidVoxelVertex;
					vertex->mExist = false;

					mVisibleVoxels.push_back( vertex );
					mVisVoxelStructure[x][y][z] = vertex;
				}
			}
		}
	}

	// Use brute force against the VoxelCloud to generate index.

	for( u32 x = 0; x < ASTEROID_SIZE - 1; x ++ )
	{
		for( u32 y = 0; y < ASTEROID_SIZE - 1; y++ )
		{
			for( u32 z = 0; z < ASTEROID_SIZE - 1; z++ )
			{
				for( u32 basex = x; basex <= x + 1; basex++ )
					for( u32 basey = y; basey <= y + 1; basey++ )
						for( u32 basez = z; basez <= z + 1; basez++ )
							// Find all triangles
							// Find second vertex, then find the next vertex using the second vertex as a starting point
							for( u32 ox = 0; ox <= 1; ox++ )
							{
								for( u32 oy = 0; oy <= 1; oy++ )
								{
									for( u32 oz = 0; oz <= 1; oz++ )
									{
										for( s32 ox1 = -1; ox1 <= 1; ox1++ )
										{
											for( s32 oy1 = - 1; oy1 <= 1; oy1++ )
											{
												for( s32 oz1 = - 1; oz1 <= 1; oz1++ )
												{
													if( basex + ox1 + ox >= x )
														if( basex + ox1 + ox <= x + 1 )
															if( basey + oy1 + oy >= y )
																if( basey + oy1 + oy <= y + 1 )
																	if( basez + oz1 + oz >= z )
																		if( basez + oz1 + oz <= z + 1 )
																			if( basex + ox1 + ox <= x + 1 )
																				if( basey + oy1 + oy <= y + 1 )
																					if( basez + oz + oz1 <= z + 1 )
																						if( basex + ox <= x + 1 )
																							if( basey + oy <= y + 1 )
																								if( basez + oz <= z + 1 )
																									if( mVisVoxelStructure[basex][basey][basez]->mExist == true )
																										if( mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mExist == true )
																											if( mVisVoxelStructure[basex + ox + ox1][basey + oy + oy1][basez + oz + oz1]->mExist == true )
																												if( mVisVoxelStructure[basex][basey][basez]->mVertex != mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mVertex )
																													if( mVisVoxelStructure[basex][basey][basez]->mVertex != mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mVertex )
																														if( mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mVertex != mVisVoxelStructure[basex + ox + ox1][basey + oy + oy1][basez + oz + oz1]->mVertex )
																														{
																															mIndex.push_back( mVisVoxelStructure[basex + ox + ox1][basey + oy + oy1][basez + oz + oz1]->mVertex );
																															mIndex.push_back( mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mVertex );
																															mIndex.push_back( mVisVoxelStructure[basex][basey][basez]->mVertex );

																															mIndex.push_back( mVisVoxelStructure[basex + ox + ox1][basey + oy + oy1][basez + oz + oz1]->mVertex );
																															mIndex.push_back( mVisVoxelStructure[basex][basey][basez]->mVertex );
																															mIndex.push_back( mVisVoxelStructure[basex + ox][basey + oy][basez + oz]->mVertex );

																														}

												}
											}
										}
									}
								}
							}

			}
		}
	}

	//Clear up after myself.
	for( auto i = mVisibleVoxels.begin(); i != mVisibleVoxels.end(); ++i )
	{
		delete(*i);
	}

	mVisibleVoxels.empty();

}


////////////////////////////////////////////////////
// Divide between AsteroidData and LargeAsteroid //
////////////////////////////////////////////////////


LargeAsteroid::LargeAsteroid() :
	mMeshData( nullptr ),
	mSceneNode( nullptr )
{

	mMeshData = new AsteroidData;

	mMeshData->generateBaseShape();
	mMeshData->generateIndexVertexBuffers();

	// set up mesh, vertex decl, entity and scene node.
	mMesh = Ogre::MeshManager::getSingleton().createManual( generateName(), "General" );
	mSubMesh = mMesh->createSubMesh();

	mMesh->sharedVertexData = new Ogre::VertexData;
	mMesh->sharedVertexData->vertexCount = mMeshData->mVertex.size();

	mDecl = mMesh->sharedVertexData->vertexDeclaration;

	size_t offset = 0;
	mDecl->addElement( 0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION );
	offset += Ogre::VertexElement::getTypeSize( Ogre::VET_FLOAT3 );

	mVBuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( offset, mMeshData->mVertex.size(), Ogre::HardwareBuffer::HBU_DYNAMIC, false );
	mIBuf = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer( Ogre::HardwareIndexBuffer::IT_32BIT, mMeshData->mIndex.size(), Ogre::HardwareBuffer::HBU_DYNAMIC );

	mMesh->sharedVertexData->vertexBufferBinding->setBinding( 0, mVBuf );
	mSubMesh->indexData->indexBuffer = mIBuf;
	mSubMesh->indexData->indexCount = mMeshData->mIndex.size();
	mSubMesh->indexData->indexStart = 0;

	//lock buffers
	f32* vertices = static_cast<f32*>( mVBuf->lock( Ogre::HardwareBuffer::HBL_DISCARD ) );

	u32 vx = 0;

	for( auto i = mMeshData->mVertex.begin(); i != mMeshData->mVertex.end(); ++i )
	{
		vertices[vx] = (*i);
		vx++;
	}

	u32* indices = static_cast<u32*>( mIBuf->lock( Ogre::HardwareBuffer::HBL_DISCARD ) );

	vx = 0;
	for( auto i = mMeshData->mIndex.begin(); i != mMeshData->mIndex.end(); ++i )
	{
		indices[vx] = (*i);
		vx++;
	}

	//unlock buffers
	mVBuf->unlock();
	mIBuf->unlock();


	mMesh->_setBounds( Ogre::AxisAlignedBox( 0, 0, 0, ASTEROID_SIZE * 100.0f, ASTEROID_SIZE * 100.0f, ASTEROID_SIZE * 100.0f ) );
	mMesh->load();

	mEntity = gRenderSystem->getSceneMgr()->createEntity( generateName(), mMesh );
	mSceneNode = gRenderSystem->getRootSceneNode()->createChildSceneNode();
	mSceneNode->attachObject( mEntity );

	gRenderSystem->getSceneMgr()->setAmbientLight( Ogre::ColourValue( 0.5, 0.5, 0.5f ) );

}

LargeAsteroid::~LargeAsteroid()
{
	gRenderSystem->getSceneMgr()->destroyEntity( mEntity );
	gRenderSystem->getSceneMgr()->destroySceneNode( mSceneNode );

	mMesh.setNull();
	mVBuf.setNull();
	mIBuf.setNull();

	//Free up memory.
	delete mMeshData;


}

cmake linking errors

17 October 2014 - 08:18 AM

I created a project demonstrating the problem I'm having: https://github.com/TheComet93/cmake-sucks

 

I have a library which links against python and boost_python. The library compiles fine, but if I try to link against my library from an executable, I get these linking errors:

$ make
[ 50%] Built target library
Linking CXX executable app
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyBytes_Size'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_FromString'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_FromFormat'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyBytes_AsString'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_InternFromString'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_FromEncodedObject'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_AsWideChar'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyModule_Create2'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_FromStringAndSize'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_AsUTF8String'
../../cmake-dep/lib/libboost_python.so: undefined reference to `PyUnicode_AsUTF8'
collect2: error: ld returned 1 exit status
app/CMakeFiles/app.dir/build.make:91: recipe for target 'app/app' failed
make[2]: *** [app/app] Error 1
CMakeFiles/Makefile2:160: recipe for target 'app/CMakeFiles/app.dir/all' failed
make[1]: *** [app/CMakeFiles/app.dir/all] Error 2
Makefile:75: recipe for target 'all' failed
make: *** [all] Error 2

Enabling verbose output reveals the core of the issue:

/usr/bin/c++       CMakeFiles/app.dir/main.cpp.o  -o app  -L/home/thecomet/documents/programming/c++/cmake-sucks/cmake-dep/lib -rdynamic ../library/liblibrary.a -lpython2.7 ../../cmake-dep/lib/libboost_python.so -Wl,-rpath,/home/thecomet/documents/programming/c++/cmake-sucks/cmake-dep/lib

liblibrary.a is being linked before python2.7 and boost_python, which shouldn't be the case.

 

I'm stuck at how I'm supposed to fix this.

 

If you look at the CMakeLists.txt files, you'll see I'm basically just doing:

add_library(library ${files})
target_link_library(library python boost_python)

and:

add_executable(app ${files})
target_link_library(app library)

This is already enough to generate these linker errors.


PARTNERS