HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

[cjass]CommonW -- know your object type in codespace

11-02-2009, 04:30 PM#1
weaaddar
This is a library that will help with usage of agent types.
It exposes several new "keywords", they are
Collapse Zinc:
typecast(type)(agent a) -> type //typecast a to type
LoadAgent(type)(table t,int pk,int ck) -> type // loads the object from the hashtable, useful as its easier to programatically write.  
typeOf(agent a) -> int // returns typeNumber of object.
typeName(agent a) -> string // returns a name of the Object.
IsType(type)(agent a) -> bool // returns true if the object is type.
SaveTypeData(type)(agent a) -> type // save the type data for an object and return it.
ReleaseTypeData(type)(agent a) -> type // release type data for an object and return it.
ReleaseTypeData(agent a) -> bool // release type data and returns true always.
Dealloc(agent a) -> bool // release type data and destroy the object. returns true if it destroyed the object. (some objects can't be destroyed).
However, unlike its predecessor DT, CommonW is not an invoked library, but a hijacker library. Simply put the goal of this library is that every single time an object enters code space it will automatically get its type data cached. And every single time you destroy an object, its type data will be removed.

CommonW also includes a macro facility to create Binary tree executions to make fast running generic agent taking functions. The binary tree executions will average ln(29)+1 or ~= 5 comparisons on average. Which is certainly better then nothing. The function included to demo this is
Collapse Zinc:
DestroyAgent(agent a) -> bool // a function that deallocs an agent type.

I am also contemplating creating a function interface and an array of that interface of size 30 so that you can define a function to execute given each type. That would suck pretty bad too. This way provided you make them generic enough you don't need to fill in array.

Why am I using Cjass?
Because the generic programming facilities in CJass are still much much better then those found in VJass/Zinc.

Warning:: CommonW is NOT done, as I'm lazy and I can't quiet figure out a good way to parse common.j and hijack and automatically replace functions for the 29 types. I've done 2 so far, and I'll probably keep working on it. But if anyone has a good way to do it, let me know.
Code:
include "cj_types.j"
define while = whilenot not;
define break = exitwhen true;
define SPACE = ;
library CommonW initializer InitW
{
    #define typecast(T) = Typecast_##T
    #define Dealloc(T) = Dealloc_##T
    #define LoadAgent(T) = Load_##T
    #define IsType(T) = IsType_##T
    #define SaveTypeData(T) = saveTypeData##T
    #define ReleaseTypeData(T) = releaseTypeData##T
    private hashtable Table = InitHashtable();
    private string array Names;
    void InitW()
    {
        int i = 0;
        Names[i++] = "UNKNOWN"
        Names[i++] = "Player"
        Names[i++] = "Destructable"
        Names[i++] = "Item"
        Names[i++] = "Unit"
        Names[i++] = "Ability"
        Names[i++] = "Timer"
        Names[i++] = "Trigger"
        Names[i++] = "TriggerCondition"
        Names[i++] = "TriggerEvent"
        Names[i++] = "Force"
        Names[i++] = "Group"
        Names[i++] = "Location"
        Names[i++] = "Rect"
        Names[i++] = "BooleanExpr"
        Names[i++] = "Sound"
        Names[i++] = "Effect"
        Names[i++] = "Quest"
        Names[i++] = "QuestItem"
        Names[i++] = "DefeatCondition"
        Names[i++] = "TimerDialog"
        Names[i++] = "Leaderboard"
        Names[i++] = "Multiboard"
        Names[i++] = "MultiboardItem"
        Names[i++] = "Trackable"
        Names[i++] = "Dialog"
        Names[i++] = "Button"
        Names[i++] = "Region"
        Names[i++] = "FogModifier"
        Names[i++] = "Hashtable"
    } 
    int typeOf(agent a)
    {
        int retVal = 0;
        retVal = LoadInteger(Table,0,GetHandleId(a)); // shortcut mode
        #if debug
            if(retVal == 0)
            {
                BJDebugMsg("Null or non-registered type");
            }
        #endif
        return retVal;
    }
    string TypeName(agent a)
    {
        return Names[typeOf(a)];
    }
    
    bool ReleaseTypeData(agent a)
    {
        RemoveSavedInteger(Table,0,GetHandleId(a));
        return true;
    }
    #define private InitSaveRet(typeNum,TypeName,T, Deconstructor) =
    {
        public int TypeName = typeNum
        

        T saveTypeData##T(T a)
        {
            SaveInteger(Table,0,GetHandleId(a),typeNum);
            return a;
        }
        
        T releaseTypeData##T(T a)
        {
            RemoveSavedInteger(Table,0,GetHandleId(a));
            return a;
        }
        

        
        T LoadAgent(T)(hashtable ht,integer pk,integer ck)
        {
            return Load##TypeName##Handle(ht,pk,ck);
        }
        
        bool IsType_##T(agent a)
        {
            return typeOf(a) == TypeName
        }

        T Typecast_##T(agent a)
        {
            T retVal = null;
            if(a != null)
            {
                SaveAgentHandle(Table,0,0,a);
                retVal = LoadAgent(T)(Table,0,0);
                RemoveSavedHandle(Table,0,0);
            }
            return retVal;
        }
        bool Dealloc_##T(agent a)
        {
            bool retVal = false;
            #if Deconstructor !=
                retVal = true;
                Deconstructor( typecast(T)(a))
            #endif
            ReleaseTypeData(a);
            return true;
        }
        
    }
    InitSaveRet(1,Pl##ayer,player,)
    InitSaveRet(2,Destructable,destructable,RemoveDestructable)
    InitSaveRet(3,Item,item,RemoveItem)
    InitSaveRet(4,Unit,unit,RemoveUnit)
    InitSaveRet(5,Ability,ability,)
    InitSaveRet(6,Timer,timer,DestroyTimer)
    InitSaveRet(7,Trigger,trigger,DestroyTrigger)
    InitSaveRet(8,TriggerCondition,triggercondition,)
    InitSaveRet(9,TriggerEvent,event,)
    InitSaveRet(10,Force,force,DestroyForce)
    InitSaveRet(11,Group,group,DestroyGroup)
    InitSaveRet(12,Location,location,RemoveLocation)
    InitSaveRet(13,Rect,rect,RemoveRect)
    InitSaveRet(14,BooleanExpr,boolexpr,DestroyBoolExpr)
    InitSaveRet(15,Sound,sound,KillSoundWhenDone)
    InitSaveRet(16,Effect,effect,DestroyEffect)
    InitSaveRet(17,Quest,quest,DestroyQuest)
    InitSaveRet(18,QuestItem,questitem,)
    InitSaveRet(19,DefeatCondition,defeatcondition,DestroyDefeatCondition)
    InitSaveRet(20,TimerDialog,timerdialog, DestroyTimerDialog)
    InitSaveRet(21,Leaderboard,leaderboard, DestroyLeaderboard)
    InitSaveRet(22,Multiboard,multiboard, DestroyMultiboard)
    InitSaveRet(23,MultiboardItem,multiboarditem, MultiboardReleaseItem)
    InitSaveRet(24,Trackable,trackable,)
    InitSaveRet(25,Dialog,dialog, DialogDestroy)
    InitSaveRet(26,Button,button,)
    InitSaveRet(27,Region,region, RemoveRegion)
    InitSaveRet(28,FogModifier,fogmodifier,DestroyFogModifier)
    InitSaveRet(29,Hashtable,hashtable,)
    
    bool IsType_##widget(agent a)
    {
        int typeval = typeOf(a);
        return (typeval == CommonW_Destructable || typeval == CommonW_Item || typeval == CommonW_Unit) 
    }
    bool IsType_##agent(agent a)
    {
        return a != null
    }
    #define CreateBinaryAgentFunc(FuncName,args,returnType,defaultVal,funcPrefix,funcargs,agentVar) = 
    {
        returnType FuncName(args)
        {
            returnType retVal = defaultVal;
            int dt_type = typeOf(agentVar);
            if(dt_type > 14)
            {
                if(dt_type < 22)
                {
                    if(dt_type < 18)
                    {
                        if(dt_type < 16){retVal = funcPrefix##sound(funcargs)}     // 15
                        elseif(dt_type < 17){retVal = funcPrefix##effect(funcargs)} // 16
                        else{retVal = funcPrefix##quest(funcargs)}                 // 17
                    }
                    elseif(dt_type < 21)
                    {
                        if(dt_type < 19){retVal = funcPrefix##questitem(funcargs)}     // 18
                        elseif(dt_type < 20){retVal = funcPrefix##defeatcondition(funcargs)} // 19
                        else{retVal = funcPrefix##timerdialog(funcargs)}              // 20                    
                    }
                    else{retVal = funcPrefix##leaderboard(funcargs)}                     // 21   
                }   
                elseif(dt_type < 26)
                {
                    if(dt_type < 24)
                    {
                        if(dt_type < 23){retVal = funcPrefix##multiboard(funcargs)}    // 22
                        else{retVal = funcPrefix##multiboarditem(funcargs)}                // 23
                    }
                    elseif(dt_type < 25){retVal = funcPrefix##trackable(funcargs)}    // 24
                    else{retVal = funcPrefix##dialog(funcargs)}                   // 25
                }
                elseif(dt_type < 28)
                {
                    if(dt_type < 27){retVal = funcPrefix##button(funcargs)}      // 26
                    else{retVal = funcPrefix##region(funcargs)}                   // 27
                }
                elseif(dt_type < 29){retVal = funcPrefix##fogmodifier(funcargs)}
                else {retVal = funcPrefix##hashtable(funcargs)}                   // 29
            }
            elseif(dt_type > 7)
            {
                if(dt_type > 11)
                    if(dt_type > 13) {retVal = funcPrefix##boolexpr(funcargs)}   // 14
                    elseif(dt_type >12) {retVal = funcPrefix##rect(funcargs)}// 13
                    else {retVal = funcPrefix##location(funcargs)}              // 12
                elseif(dt_type > 9)     
                    if(dt_type > 10) {retVal = funcPrefix##group(funcargs)}    // 11
                    else {retVal = funcPrefix##force(funcargs)}              // 10
                elseif(dt_type > 8)  {retVal = funcPrefix##event(funcargs)}   // 9
                else {retVal = funcPrefix##triggercondition(funcargs)}                  // 8
            }
            elseif(dt_type > 3)
            {
                if(dt_type > 5)
                    if(dt_type > 6){retVal = funcPrefix##trigger(funcargs)} // 7
                    else {retVal = funcPrefix##timer(funcargs)}  // 6
                elseif(dt_type > 4){retVal = funcPrefix##ability(funcargs)}     // 5
                else{retVal = funcPrefix##unit(funcargs)}    // 4
            }
            elseif(dt_type > 1)
                if(dt_type > 2){retVal = funcPrefix##item(funcargs)}
                else{retVal = funcPrefix##destructable(funcargs)}
            elseif(dt_type > 0){retVal = funcPrefix##player(funcargs)} // 1 
            return retVal;
        }
    }
    CreateBinaryAgentFunc(DestroyAgent,<agent a>,bool,false,Deallo##c_,a,a);
}// macro hell. We now go and redefine everything to use CommonW expressive nature.
// Player Natives
#define GetFilterPlayer() = SaveTypeData(player)(GetFilterPla##yer())
#define GetEnumPlayer() = SaveTypeData(player)(GetEnumPla##yer())
#define GetWinningPlayer() = SaveTypeData(player)(GetWinningP##layer())
#define GetTournamentFinishNowPlayer() = SaveTypeData(player)(GetTournamentFinishNowPla##yer())
#define GetTriggerPlayer() = SaveTypeData(player)(GetTriggerPla##yer())
#define GetChangingUnitPrevOwner() = SaveTypeData(player)(GetChangingUni##tPrevOwner())
#define GetEventDetectingPlayer() = SaveTypedata(player)(GetEventDetec##tingPlayer())
#define GetItemPlayer(it) = SaveTypeData(player)(GetItemPl##ayer(it))
#define GetOwningPlayer(Unit) = SaveTypeData(player)(GetOwningPla##yer(Unit))
#define <Player>(i) = SaveTypeData(player)(Play##er(i))
#define GetLocalPlayer() = SaveTypeData(player)(GetLocalPlaye##r())
#define LoadPlayerHandle(table,pk,ck) = SaveTypeData(player)(LoadPlayer##Handle(table,pk,ck))
//Destructable Natives
#define GetFilterDestructable() = SaveTypeData(destructable)(GetFilterDestr##uctable())
#define GetEnumDestructable() = SaveTypeData(destructable)(GetEnumDestr##uctable())
#define GetOrderTargetDestructable() = SaveTypeData(destructable)(GetOrderTargetDestru##ctable())
#define GetSpellTargetDestructable() = SaveTypeData(destructable)(GetSpellTargetDestr##uctable())
#define CreateDestructable(objectid,x,y,face,scale,variation) = SaveTypeData(destructable)(CreateDestr##uctable(objectid,x,y,face,scale,variation))
#define CreateDestructableZ(objectid,x,y,z,face,scale,variation) = SaveTypeData(destructable)(CreateDestr##uctableZ(objectid,x,y,z,face,scale,variation))
#define CreateDeadDestructable(objectid,x,y,face,scale,variation) = SaveTypeData(destructable)(CreateDeadDestr##uctable(objectid,x,y,face,scale,variation))
#define CreateDeadDestructableZ(objectid,x,y,z,face,scale,variation) = SaveTypeData(destructable)(CreateDeadDestr##uctableZ(objectid,x,y,z,face,scale,variation))
#define GetTriggerDestructable() = SaveTypeData(destructable)(GetTriggerDestr##uctable())
#define GetUnitRallyDestructable(whichUnit) = SaveTypeData(destructable)(GetUnitRallyDes##tructable())
#define LoadDestructableHandle(table,pk,ck) = SaveTypeData(destructable)(LoadDestru##ctableHandle(table,pk,ck))
#define RemoveDestructable(d) = ReleaseTypeData(destructable)(RemoveDestruc##table(d))
#define KillDestructable(d) = ReleaseTypeData(destructable)(KillDestructab##le(d))
11-04-2009, 02:00 AM#2
weaaddar
I guess nobody actually can see a need for this?
11-04-2009, 02:21 AM#3
Rising_Dusk
I really think it is actually just because you chose to use cJASS.
11-04-2009, 12:00 PM#4
weaaddar
It's not like it was a choice, what I'm doing here can't be done in VJass :(
11-04-2009, 01:25 PM#5
Vexorian
I just don't think the world is ready for this :).

Think about it, most of the cjass types are guys that thought define is a way to save execution speed through making all functions inline-able... , and thus would not appreciate that you are introducing this functionality in exchange of a lot of overhead so they would avoid it like the plague. The rest won't even understand the code...

For what's worth the major problem here is that after all the things that happened to us thanks to handle stack corruption bugs due to misusing I2H, we got used to actually knowing what the types of our agents are.

I personally can't think of good uses for it, this may be because we pretty much got used to knowing the type of the agents we are using at run time... I also think this is best done completely at compile time with true generics and specialization, just give myself about a week before we have something like generics that will sort of work, I've been having mind explosions regarding how to implement it for a while...

Bleh I figured out hooks really need a way to get the native's return value.
11-04-2009, 02:14 PM#6
Anitarf
Quote:
Originally Posted by Vexorian
Bleh I figured out hooks really need a way to get the native's return value.
Wait, do hooks run before or after the native? I was never really clear on that.
11-04-2009, 02:19 PM#7
Rising_Dusk
Before, otherwise they wouldn't make a lot of sense in say, hooking RemoveUnit.
11-04-2009, 02:27 PM#8
Vexorian
Quote:
Originally Posted by Anitarf
Wait, do hooks run before or after the native? I was never really clear on that.
The answer would be like grim says, make an after hook that takes just the return value, I guess.
11-05-2009, 12:07 AM#9
weaaddar
Well, I'm that crazy guy that uses CJASS for
#if which is better then static if as it can read parameters in macro declarations and tell you if they are unused.
++ //although admittedly I can live without it.
macros to emulate templates.
function declarations like those in C.
Macros to default argument functions. // this is just so I can be lazy

Oh and the all important Macros calling other macros. Which is awesome when it works. As then you don't actually have to remember what List!(T) is defed as internally.
As for the other thing, there are still things you couldn't do without knowing type information. How can you have a generic destroy agent? Or load agent handle (which isn't implemented but one could just hook all the saver of agent types and add that you save an integer with type info at runtime, so you can do the execution in 5 calls instead of 29).

Yes, its an overhead, but its a really neat bit of functionality. I don't think anyone except for adolf uses CJass for "inlining functions".