Sign in to follow this  

Translating template interface from C++ to C#

This topic is 3872 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I was translating a bit of code from C++ to C# when I came across a minor difference between generics and templates that I wouldn't mind settling. The overall structure is that of an generic asset manager coupled with a generic abstract asset class. A simplified example is this:
class CAssetManager<BASETYPE>
	{
	}
	
abstract class IAsset<TYPE>
	{
	abstract bool Load();
	abstract bool Release();
	IAsset(string FileName,ref CAssetManager<TYPE> Manager)	{}
	}
At this point I can derive a class from IAsset for whatever asset type I want, say, a bitmap (using a made up datatype BITMAPDATA)
class BitmapAsset : IAsset<BITMAPDATA>
	{
	//overriden methods here
	}
Now, I can simply create an instance of a bitmap manager and bitmap assets like so.
CAssetManager<BITMAPDATA> BitmapManager = new CAssetManager<BITMAPDATA>();
BitmapAsset Bitmap = new BitmapAsset("C:/image.txt",ref BitmapManager);
The trouble is, I must declare BITMAPDATA data in the creation of CAssetManager when it would make more sense to declare the type "BitmapAsset" like so.
CAssetManager<BitmapAsset> ...
From here I do not understand how I can, within the manager class, access to type that was passed to the base asset class when creating the derived one. In C++ all I need to do is typdefine it in the asset class and then use the :: operator to access that variable from the type passed to the manager class. Of course, C# doesn't have typedefs but I imagine with all this neat stuff, like generics being 1st class objects and RTTI, I can do something similar. I realize this isn't a big issue but since I'm just starting to learn C# I'd like to know as much as possible about this sort of thing.

Share this post


Link to post
Share on other sites
Be warned: Generics are not templates nor are they anything like templates. Generics are a runtime construct with a bit of compiletime sanity checking. Templates are an entirely compiletime construct with no runtime anything.

As for your problem, just have your abstract IAsset class have a method that returns a Type obtained through the typeof instruction on the generic-parameter.

Share this post


Link to post
Share on other sites
Hmmmmm, ok. Now you've lost me. I understand the method returning the type but I can't seem to wrap my head around the syntax needed within the manager class.
Maybe you can point it out for me. I've tried using the "where" contraint for BASETPYE to be derived from "IAsset<TYPE>", "TYPE" must be defined alongside BASETYPE which defeats the purpose of passing a derived class instead of the basetype to begin with.

I've read that generics are based on runtime information many times but I think all those years of C++'s half-assed-everything rotted my brains out. I can't seem to quite comprehend how it works. I can only guess that when a generic type is created it's type is created along with it and treated as an object in its own right.

IAsset.cs

using System;
using System.Collections.Generic;
using System.Text;

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("CAssetManager")]

namespace AssetControl
{
enum RESELEMENTSTATE
{
RES_EMPTY = 0,
RES_RESERVED,
RES_LOADED,
};


enum RESLOCKSTATE
{
RESLSTATE_UNLOCKED,
RESLSTATE_LOCKED,
};


abstract class IAsset<TYPE>
{
private RESELEMENTSTATE m_State = RESELEMENTSTATE.RES_EMPTY;
private RESLOCKSTATE m_LockState = RESLOCKSTATE.RESLSTATE_UNLOCKED;
private string m_FileName;
private TYPE m_Data;
private CAssetManager<TYPE> m_Manager;

static public Type MyType()
{
return(typeof(TYPE));
}

protected internal abstract bool Load(string FileName,out TYPE Data);
protected internal abstract bool Release();


public string FileName
{
get {
return(m_FileName);
}
}


public int Lock(out TYPE Data)
{
m_LockState = RESLOCKSTATE.RESLSTATE_LOCKED;
Data = m_Data;
return (0);
}


public int Unlock()
{
m_LockState = RESLOCKSTATE.RESLSTATE_UNLOCKED;
return (0);
}


public IAsset(string FileName,ref CAssetManager<TYPE> Manager)
{
//assert Manager reference
if(Manager == null)
{
throw new Exception(AssetFailCode.ManagerRefNotValid);
}

//set these states first, that way they may
//be accessed within the derived class's "Load()" method
m_State = RESELEMENTSTATE.RES_LOADED;
m_FileName = FileName;

//register and assign asset manager classes, making sure
//not to create an asset that already exists
m_Manager = Manager;
try {
//this will internally call the "Load()" method,
//and throw an exception if it fails
m_Manager.Register(this,out m_Data);
}
catch(Exception e)
{
//load failed, reset to default states and throw exception
m_State = RESELEMENTSTATE.RES_EMPTY;
m_FileName = null;
throw e;
}
}


~IAsset()
{
//assert that the Manager reference was valid when created
if(m_Manager == null)
{
if(!(Release()))
{
throw new Exception(AssetFailCode.AssetLoadFail);
}
return;
}


try {
//calls "Release()" internally
m_Manager.Unregister(this);
}
catch(Exception e)
{
throw e;
}
}
}
}




CAssetManager.cs

using System;
using System.Collections.Generic;
using System.Text;

[assembly:System.Runtime.CompilerServices.InternalsVisibleTo("IAsset")]


namespace AssetControl
{
class AssetFailCode
{
public const string AssetLoadFail = "Asset Failed To Load Properly";
public const string AssetReleaseFail = "Asset Failed To Release Properly";
public const string ManagerRefNotValid = "Manager Reference Passed As NULL";
}


public class DataRefCountPair<BASETYPE>
{
public BASETYPE Data;
public int RefCount;

public DataRefCountPair()
{
}
public DataRefCountPair(BASETYPE data,int refcount)
{
Data = data;
RefCount = refcount;
}
}


class CAssetManager<BASETYPE>
{
//maps asset filenames to a reference
//of the asset's data and a refcount
private SortedDictionary<string,DataRefCountPair<BASETYPE>> m_Database = new SortedDictionary<string,DataRefCountPair<BASETYPE>>();


protected internal void Register(IAsset<BASETYPE> Asset, out BASETYPE Data)
{
DataRefCountPair<BASETYPE> Entry;

//find out if file was previously loaded by checking database.
//If it was, asign the asset-class's data to that entry
if(!(m_Database.TryGetValue(Asset.FileName,out Entry)))
{
//if the file was not already loaded, load it now
if(Asset.Load(Asset.FileName,out Data))
{
//now assign the loaded asset-class's
//data to the manager's database
Entry = new DataRefCountPair<BASETYPE>(Data,1);
m_Database.Add(Asset.FileName, Entry);
}
else {
throw new Exception(AssetFailCode.AssetLoadFail);
}
}
else {
//assign asset-class's Data to
//the pre-existing element and
//increment the refcount
Data = m_Database[Asset.FileName].Data;
m_Database[Asset.FileName].RefCount++;
}
}


protected internal void Unregister(IAsset<BASETYPE> Asset)
{
//reduce refcount by one, if refcount reaches zero
//set that element to NULL and remove it from the database
DataRefCountPair<BASETYPE> Entry;
m_Database.TryGetValue(Asset.FileName,out Entry);

if(Entry.RefCount == 1)
{
m_Database.Remove(Asset.FileName);
}
else {
m_Database[Asset.FileName].RefCount--;
}


if(!(Asset.Release()))
{
throw new Exception(AssetFailCode.AssetReleaseFail);
}
}
}

}




BitmapAsset.cs (in this case I was testing by readin in a byte froma file and storing it in an int)

using System;
using System.Collections.Generic;
using System.Text;

using AssetControl;
using System.IO;


namespace AssetFactory
{
class BitmapAsset : IAsset<int>
{
protected internal override bool Load(string FileName,out int Data)
{
//FileStream f = new FileStream(FileName,FileMode.Create,FileAccess.Write);
FileStream f = new FileStream(FileName,FileMode.Open,FileAccess.Read);
byte[] data = new byte[1];
data[0] = 99;
//f.Write(data,0,1);
f.Read(data,0,1);

Data = data[0];

Console.WriteLine("-File Loaded\nFile Name: {0}\nValue Read: {1}\n",FileName,data[0]);

return true;
}


protected internal override bool Release()
{
Console.WriteLine("-Bitmap Released Successfully");
return true;
}

public BitmapAsset(string FileName,ref CAssetManager<int> Manager) : base(FileName,ref Manager)
{
}
}
}


Share this post


Link to post
Share on other sites

This topic is 3872 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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

Sign in to follow this