| 10-01-2009, 05:10 AM | #1 |
I've created a small little set of macros to ease the pain of lacking templates, lacking hashtable structs, and other still features to come. The idea is to basically create simple one-liner methods that can be called while passing type information. These oneliners are simple, and will be inlined by vJass's optimizer, however we give the object's a uniform naming schema allowing one to call them as if they were generic. Once you have this Library in play it becomes easy to create generic classes. However, there are some cons as these aren't truly natively supported you must mention type information for inheriting types. Registering a type requires passing 6 pieces of information, the type,typename that appears in HT functions, method to return a hash value, basename that appears in HT functions,defaultvalue, and a to string method) e.g. int,Integer,,Integer,0,I2S unit,UnitHandle,GetHandleId,Handle,null,GetUnitName string,Str,StringHash,String,null, item,ItemHandle,GetHandleId,Handle,null,GetItemName StructType,Integer,StructType.StaticMethodThatReturnsInteger,Integer,0,StructType.StaticMethodThatRe turnsString Here is the entire library:: Code:
define <GetHashCode>(T) = GetHashCode_##T;
define <HashTableLoad>(T) = HashTableLoad_##T;
define <HashTableSave>(T) = HashTableSave_##T;
define <HashTableHave>(T) = HashTableHave_##T;
define <HashTableRemove>(T) = HashTableRemove_##T;
define <DefaultVal>(T) = DefaultVal_##T();
define <Stringify>(T) = Stringify_##T;
define RegisterType(T, TypeName,HashMethod, BaseName,DefaultValue,TtoString) =
{
T HashTableLoad(T)(hashtable ht,int pk,int ck)
{
return Load##TypeName(ht,pk,ck)
}
void HashTableSave(T)(hashtable ht,int pk,int ck,T value)
{
Save##TypeName(ht,pk,ck,value);
}
bool HashTableHave(T)(hashtable ht,int pk,int ck)
{
return HaveSaved##BaseName(ht,pk,ck);
}
void HashTableRemove(T)(hashtable ht,int pk,int ck)
{
RemoveSaved##BaseName(ht,pk,ck);
}
int GetHashCode(T)(T key)
{
return HashMethod(key);
}
constant T DefaultVal_##T()
{
return DefaultValue;
}
string Stringify(T)(T obj)
{
return TtoString(obj);
}
}And some usage examples:: JASS:RegisterType(int,Integer,,Integer,0,I2S); RegisterType(string,Str,StringHash,String,null,); RegisterType(unit,UnitHandle,GetHandleId,Handle,null,GetUnitName); Great now why exactly would you want this in your map? So you can make all sorts of interesting types. For instance, here is Dict(Tk,Tv) and List(T). Dict(Tk,Tv) is like Vex's table, except it takes any type for the key, and any type for the value. And List(T) is a fixed capacity or infinite length array with some helper functions thrown in for good measure. Simplify calling section:: Code:
define Dict(Tk,Tv) = Dict_##Tk##Tv SPACE define <new Dict>(Tk,Tv) = Dict(Tk,Tv).create define List(T) = List_##T SPACE define <new List>(T) = List(T).create InitDict:define InitDict(Tk,Tv) = { struct Dict(Tk,Tv) private static hashtable Table = InitHashtable() Tv operator[](Tk key) { return HashTableLoad(Tv)(.Table,integer(this),GetHashCode(Tk)(key)); } void operator[]=(Tk key, Tv value) { HashTableSave(Tv)(.Table,integer(this),GetHashCode(Tk)(key),value); } void Remove(Tk key) { HashTableRemove(Tv)(.Table,integer(this),GetHashCode(Tk)(key)); } bool ContainsKey(Tv key) { return HashTableHave(Tv)(.Table,integer(this),GetHashCode(Tk)(key)); } void Clear() { FlushChildHashtable(.Table,integer(this)); } static string AsString(thistype dict) { return I2S(integer(dict)); // no string, but I need to return something. } private void onDestroy() { .Clear(); } } InitList method:define InitList(T) = { struct List(T) private int m_Size; private int m_Capacity; private static hashtable Table = InitHashtable() int operator Size() { return .m_Size; } int operator Capacity() { return .m_Capacity; } T operator[](int index) { T retVal = DefaultVal(T); if( - 1 < index and index < .m_Size) retVal = HashTableLoad(T)(.Table,integer(this),index) return retVal; } void operator[]=(int index,T value) { if( -1 < index and index < .m_Size) HashTableSave(T)(.Table,integer(this),index,value) } bool Add(T obj) { bool retVal = false; if(.m_Capacity == 0 || .m_Size < .m_Capacity) // this may no longer be neccessary, but I haven't checked // what cJass compiles this section in to. this[++.m_Size-1]= obj; retVal = true; return retVal; } T RemoveAt(int index) { T retVal = DefaultVal(T); if(-1 < index and index < .m_Size) retVal = this[index]; int i = index-1; while(++i < .m_Size - 1) { this[i] = this[i+1]; } .m_Size--; HashTableRemove(T)(.Table,integer(this),.m_Size); return retVal; } // Performs at O(1) time, but moves things about // in the process. T UnStableRemoveAt(int index) { T retVal = DefaultVal(T); if( - 1 < index and index < .m_Size) retVal = this[index]; this[index] = this[-1+.m_Size--]; HashTableRemove(T)(.Table,integer(this),.m_Size); return retVal; } int IndexOf(T value) { int i = -1; int retVal = -1; while(++i < this.Size) if(this[i] == value) { retVal = i; break; } return retVal; } bool Contains(T value) { return .IndexOf(value) >= 0; } bool Remove(T value) { int index = .IndexOf(value); if(index > -1) .RemoveAt(index); return (index > -1); } void Clear() { FlushChildHashtable(.Table,integer(this)); } string ToString() { string retVal = ""; int i = -1; string comma = ""; while(++i < this.Size) retVal+= comma+Stringify(T)(this[i]); comma = ","; return retVal; } static string AsString(thistype list) { return list.ToString(); } private void onDestroy() { .Clear() } // This is a shallow clone static thistype Clone(thistype list) { thistype retVal = thistype.create(list.Capacity); int i = -1; while(++i < list.Size) retVal.Add(list[i]); return retVal; } // Yes, I suppose I could've wrote List(T) but hey thistype sweet! // passing 0 creates a list without a fixed cap. static thistype create(int capacity) { thistype retVal = thistype.allocate(); retVal.m_Capacity = capacity; return retVal; } } These can be applied to any registered type. You can even register these complex types. JASS:RegisterType(List(int),Integer,,Integer,0,List(int).AsString); RegisterType(Dict(int,int),Integer,,Integer,0,Dict(int,int).AsString); InitList(int); InitList(List(int)); InitDict(int,int); InitList(Dict(int,int)); I'm not going to bother submitting these as resources on the site as I realize only 4 wierdos like cJass, and I'm sure there exist efficient Lists and Dictionaries. These were merely examples and proof of concepts. Unfortunately, this objects will slow down if you try to use larger sizes just like normal vJass objects. |
| 10-01-2009, 10:52 PM | #2 |
It's common to use angle brackets for generic types: Cup<T> instead of Cup(T). You should make the register type macro private. "RegisterType" is sortof a generic name, and If people want more types they should be putting the calls inside library not outside of it. |
| 10-02-2009, 02:36 AM | #3 |
D's templates are in parens. However, the real reason I'm using parens is that cJass currently doesn't have templatatic macro definitions. Anyway this is just a way to create a uniform calling pattern, so you can use hashtables in macros/defines without constantly having to use multiple definitions. I included a strigifier as I felt it was a common need, stringifying things. Hashing is also useful for storing as keys. The rest of the methods are the standard api. |
