I just finished my proof of concept:
http://gm.maartenbaert.be/shared_buffers.zip
I've tested it in GCC and VC++, both versions are compatible.
The main DLL (shared_buffers.dll) exports buffer functions to GM, so games can access the buffers too:
shared_buffers_get_handle()buffer_create()buffer_destroy(id)buffer_exists(id)buffer_to_string(id)buffer_get_pos(id)buffer_get_length(id)buffer_at_end(id)buffer_get_error(id)buffer_clear_error(id)buffer_clear(id)buffer_set_pos(id,pos)buffer_read_from_file(id,filename)buffer_write_to_file(id,filename)buffer_read_int8(id)buffer_read_uint8(id)buffer_read_int16(id)buffer_read_uint16(id)buffer_read_int32(id)buffer_read_uint32(id)buffer_read_int64(id)buffer_read_uint64(id)buffer_read_float32(id)buffer_read_float64(id)buffer_write_int8(id,value)buffer_write_uint8(id,value)buffer_write_int16(id,value)buffer_write_uint16(id,value)buffer_write_int32(id,value)buffer_write_uint32(id,value)buffer_write_int64(id,value)buffer_write_uint64(id,value)buffer_write_float32(id,value)buffer_write_float64(id,value)buffer_read_string(id)buffer_write_string(id,string)buffer_read_data(id,len)buffer_write_data(id,string)buffer_read_hex(id,len)buffer_write_hex(id,string)buffer_write_buffer(id,id2)buffer_write_buffer_part(id,id2,pos,len)
Please tell me if I forgot something important. The functions that can be called by the other DLLs are almost the same, but they use pointers instead of ids:
Buffer* Create();void Destroy(Buffer* buffer);unsigned int GetID(Buffer* buffer);Buffer* Find(unsigned int id);const char* ToString(Buffer* buffer);char* GetData(Buffer* buffer);unsigned int GetPos(Buffer* buffer);unsigned int GetLength(Buffer* buffer);bool IsAtEnd(Buffer* buffer);bool GetError(Buffer* buffer);void ClearError(Buffer* buffer);void Clear(Buffer* buffer);void SetPos(Buffer* buffer, unsigned int newpos);void SetLength(Buffer* buffer, unsigned int newlength);bool ReadFromFile(Buffer* buffer, const char* filename);bool WriteToFile(Buffer* buffer, const char* filename);int8_t ReadInt8(Buffer* buffer);uint8_t ReadUint8(Buffer* buffer);int16_t ReadInt16(Buffer* buffer);uint16_t ReadUint16(Buffer* buffer);int32_t ReadInt32(Buffer* buffer);uint32_t ReadUint32(Buffer* buffer);int64_t ReadInt64(Buffer* buffer);uint64_t ReadUint64(Buffer* buffer);float ReadFloat32(Buffer* buffer);double ReadFloat64(Buffer* buffer);void WriteInt8(Buffer* buffer, int8_t value);void WriteUint8(Buffer* buffer, uint8_t value);void WriteInt16(Buffer* buffer, int16_t value);void WriteUint16(Buffer* buffer, uint16_t value);void WriteInt32(Buffer* buffer, int32_t value);void WriteUint32(Buffer* buffer, uint32_t value);void WriteInt64(Buffer* buffer, int64_t value);void WriteUint64(Buffer* buffer, uint64_t value);void WriteFloat32(Buffer* buffer, float value);void WriteFloat64(Buffer* buffer, double value);const char* ReadString(Buffer* buffer);void WriteString(Buffer* buffer, const char* string);void ReadData(Buffer* buffer, char* ptr, unsigned int bytes);void WriteData(Buffer* buffer, const char* ptr, unsigned int bytes);void ReadHex(Buffer* buffer, char* ptr, unsigned int bytes);void WriteHex(Buffer* buffer, const char* ptr, unsigned int bytes);void WriteBuffer(Buffer* buffer, Buffer* source);void WriteBufferPart(Buffer* buffer, Buffer* source, unsigned int pos, unsigned int bytes);
The functions GetID and Find can be used to convert pointers to ids and vice versa.
The function shared_buffers_get_handle() is used to pass the pointer to the other DLL:
shared_buffers_init();
test_init();
// initialize shared buffers interface for test.dll
test_init_shared_buffers(shared_buffers_get_handle());
Now the other DLL can use the pointer to manipulate buffers:
#include "gm.h"
#include "SharedBuffers.h"
SharedBufferInterface *sbi = NULL;
gmexport double test_init_shared_buffers(double handle) {
sbi = (SharedBufferInterface*)(gm_double_to_uint(handle));
return 1;
}
gmexport double test_writetobuffer(double id) {
if(sbi == NULL) return 0;
Buffer *b = sbi->Find(gm_double_to_uint(id));
if(b == NULL) return 0;
sbi->WriteInt32(b, 12345);
sbi->WriteString(b, "Hello world");
sbi->WriteFloat32(b, 42.42f);
return 1;
}
Memory management is done by shared_buffers.dll, other DLLs can't allocate or free memory. They should call the Resize function.
Using it in other DLLs is very simple, just include SharedBuffers.h. That's it.
This system has the added benefit of modularity: If some user doesn't need the buffer functionality, that user can choose not to add shared_buffers.dll to his/her game. The part of the DLL that doesn't need buffers will still work.
We could also use shared memory to pass the pointer of the struct, or even to store the entire struct, but I think it's easier to simply pass the pointer through GM. If we use shared memory we would still need functions to tell DLLs to load the shared memory at the right time, because it's possible some DLLs are loaded before shared_buffers.dll is loaded.
What do you think? Would you use something like this in your DLLs? What should be changed to make it better?
Edited by Maarten Baert, 19 April 2011 - 10:53 AM.