Jump to content
The simFlight Network Forums

Expose IOffset


swemaniac

Recommended Posts

Hi Paul,

 

Is there a chance you can expose the FSUIPC.IOffset interface or perhaps enclose all offsets in another (marker) interface? The reason I'm asking is right now it's impossible to abstract an instance of Offset<T> (say you want to push offsets into a list or a dictionary for caching purposes, or instantiate offsets at runtime). Right now I'm casting object:s and doing some serious generic type juggling (good fun but not really pretty).

 

Cheers

/Johan

Link to comment
Share on other sites

Hi Johan,

 

Yes it's possible to make IOffset public.

 

Before I do, I want to point out that the 'Value' property of IOffset is a generic 'object', so you'll likely have to cast the value if you want to do anything with it apart from ToString(). The data type can be accessed from a property on IOffset. I just want to check if that's okay for what you're doing, or will it just move the problem to elsewhere in the code?

 

Paul

Link to comment
Share on other sites

Hi,

 

Thank you I appreciate your input and you got a valid point, and I can explain why making IOffset public would make life easier for me if it interests you:

 

Consider a simple dictionary of offset addresses and offsets. Right now it has to be declared as such:

readonly IDictionary<int, object> Offsets = new Dictionary<int, object>();

(As there are no common public denominator). Could be done with dynamic as well.

 

Then consider the method needed to retrieve an Offset<T> value from that dictionary.

 

The easy implementation is if T is known (compile time):

T GetOffsetValue<T>(int address)
{
	object offset;

	if (Offsets.TryGetValue(address, out offset))
	{
		if (offset is Offset<T>)
			return ((Offset<T>)offset).Value;
		else
			throw new InvalidOperationException(string.Format("Value of requested offset {0} does not match the expected type {1}", address, typeof(T)));
	}

	Offsets.Add(address, new Offset<T>(address));
	return default(T);
}

But if you don't know T (as in getting an Offset from a Type), the code becomes a little messier (note that the generic type creation isn't strictly needed to see if the value is of the correct type, but it enforces the dictionary type to be Offset<>):

object GetInternalOffsetValue(int address, Type type)
{
	object tempOffset;

	var offsetBaseType = typeof(Offset<>);
	var offsetType = offsetBaseType.MakeGenericType(type);

	if (Offsets.TryGetValue(address, out tempOffset))
	{
		if (offsetType.IsInstanceOfType(tempOffset))
			return tempOffset.GetType().GetProperty("Value").GetGetMethod().Invoke(tempOffset, null);
		else
			throw new InvalidOperationException(string.Format("Value of requested offset {0} does not match the expected type {1}", address, type));
	}

	var newOffset = Activator.CreateInstance(offsetType);
	Offsets.Add(address, newOffset);

	if (type.IsValueType)
		return Activator.CreateInstance(type);

	return null;
}

I am especially unfond of the magic string needed to get the value :)

 

If IOffset was public, the dictionary can be strongly typed, and the second method can be trivialized by comparing the data type directly instead of having to create a generic type on the fly, and the offset value can be accessed directly via IOffset.Value rather than relying on a reflected magic string "Value".

Link to comment
Share on other sites

return tempOffset.GetType().GetProperty("Value").GetGetMethod().Invoke(tempOffset, null);

Ouch! Yes, I see the problem now. New version attached with public IOffset.

 

Thanks for the explanation, it's always interesting to hear how people are using the DLL, especially if it's beyond the basic usage.

 

Thinking more about your requirements, I could create a non-generics version of Offset and let you specify the address and length then just get the raw bytes back. I could put methods like GetInt32(), GetDouble() etc to convert the raw bytes to a proper type. A bit like the DataReader for database connections if you're familiar with that. Would that make it even easier for you?

 

The generics are great for the normal usage where you know the offsets at design time, but I can see that they get in the way if you're creating offsets at runtime.

 

Paul

FSUIPCClient3.0_BETA.zip

Link to comment
Share on other sites

Thanks Paul!

 

I like the idea of a more "raw" offset object that's untyped. How about "Offset(address, length)" with "T Get<T>" and "object Get(Type)" or something to accommodate both generics and non-generics users? I think it's pretty common to see overloads for "Type" methods wherever there's a generic method/class. You could still have the helper methods GetInt32() etc, but I prefer giving a type (again, easier in runtime).

 

Then we could do:

var offset = new Offset(0x345, 10);
byte[] raw = offset.Value;
string val = offset.Get<string>();
string val2 = (string)offset.Get(typeof(string));
Link to comment
Share on other sites

string val = offset.Get<string>();

Yes, I like that better; it's much cleaner.

 

To do this properly the plain non-generic offset should be the base class to the generic offsets (so a replacement for IOffset). This will need a bit of refactoring work in the DLL, so I'll need a week or so to do this. Just giving you fair warning as it'll be a breaking change if you decide to continue down the IOffset route for now.

 

Thanks,

 

Paul

Link to comment
Share on other sites

Hi Johan,

 

Attached is a new version with a non-typed Offset class. IOffset has been removed.

 

The declaration is the same for Offset<T>, except that all constructors require the length to be specified.

 

Instead of the strongly-typed Value property that Offset<T> has, you get and set the value via two methods:

 

GetValue<T>()

 

Gets the value from the offset, converting the raw bytes into type T. The only types allowed here are those allowed for Offset<T>. Note that if you want access to the raw byte array you can pass Byte[] as T.

 

 

SetValue(object NewValue)

 

Creates the raw bytes from whatever object is passed in. Again the types are limited to those allowed for Offset<T>. You can just pass a raw byte array if you need to.

 

 

Writing works the same way as before; after calling SetValue() the offset will be in Write mode for the next process().

 

Below is some sample code showing two uses of the new class.

        private Offset avionics = new myOffset(0x2E80, 4);
        private Offset aircraftName = new Offset(0x3D00, 256);
        private void read_Click(object sender, EventArgs e)
        {
            FSUIPCConnection.Process();
            this.checkBox1.Checked = (avionics.GetValue<int>() == 1);
            this.label1.Text = aircraftName.GetValue<string>();
        }

        private void write_Click(object sender, EventArgs e)
        {
            avionics.SetValue(this.checkBox1.Checked ? 1 : 0);
            FSUIPCConnection.Process();
        }

I haven't 'sealed' the Offset class so if you need to store your own information about the offsets, or add your own methods, you can inherit it and make your own offset class.

 

Paul

Link to comment
Share on other sites

Paul,

 

I'm missing the thing that was going to help me access offset values at runtime without knowing the underlying type. I.e. I know the type I need to access, but i can't use `T` at runtime. I need to be able to get an `object` (as explained above in my examples) so that I can convert it to a type. E.g. `Convert.ChangeType(offset.GetValue(), type)` (where type is for instance, typeof(int)).

 

Could you perhaps include a non-generic version of the `GetValue` method that returns the value T as an `object`?

Link to comment
Share on other sites

Could you perhaps include a non-generic version of the `GetValue` method that returns the value T as an `object`?

 

 

 

Okay - from your previous post you have this:

string val2 = (string)offset.Get(typeof(string));

Is this what you want?

object Offset.GetValue(Type returnAsType);

Paul

Link to comment
Share on other sites

Sorry, yes. The scenario I'm referring to is basically this:

// creating the offset
var offset = new Offset(123, 4); // or var offset = new Offset<int>(123);

// somewhere else, getting the value back of a type that's determined at runtime
internal object GetInternalOffsetValue(int address, Type type)
    return offset.GetValue(type);

Does it make sense to you to add that method? The GetValue<T> is still very useful to me, it's just I need the non-generic method as well to make this work.

Link to comment
Share on other sites

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
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use. Guidelines Privacy Policy We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.