- MarcusM-PDX
- Posts: 7
- Joined:
Custom TypeConverter for Wrapper to String
NOTE: A few posts down I show a working example of a TypeConverter, for those of you in the future looking for one. :)
Greetings there, I'm attempting to write a TypeConverter in C++ that will allow me to bind a particular wrapper into Strings. There's a bit of sanity there, I promise.
Below is a sanitized/simplified form of the wrapper. In essence, life is made much easier if we have a singular wrapper which, if given a LocalizationKey stores the localized text generated from that key, but also it has the ability to have text applied that isn't supposed to be localized (ex: a user typed in a name for something). The actual functionality of this wrapper is much more than this, but this is the pertinent aspects of the wrapper for this usecase. I should further clarify, while yes, I CAN just always bind through the Wrapper into OutputText, the design requirements from the rest of my team prohibit this. (In short they want to do {Binding Group.Name} and not {Binding Group.Name.OutputText}.)
Note 1: This part works fine inside of my own code, where I may well be trying to do something like "Noesis::String Tester = LoclizationWrapper("TestText");". The problem is that there are various automated bits within Noesis itself that basically says "The left side is type 'Noesis::String' and the right side is type 'LocalizationWrapper', I'm not going to try this without a TypeConverter. Binding failed.".
Below is the simplified example of the TypeConverter inside the header.
Note 2: This line fails to compile with a message of "Class 'Noesiss::TypeMetaData' may not have a template argument list.'.
When I expand out the macro, the error arises from the following line:
The compiler REALLY does not want "SelfClass" to exist there. But if I keep the line and remove that, then it seems happy (if theoretically wrong?). But then, with some further mucking about with the code in the .CPP file once I expand out the NS_IMPLEMENT_REFLECTION() macro, I can get the .h/.cpp file to compile in isolation, but when trying to compile the project as a whole, the complaint that I get is that "StaticFillClassType does not exist in global namespace", at the location in TypeClassCreator.inl where it's trying to execute the templated "void TypeClassCreator::Fill(Type* type)", specifically "ClassT::StaticFillClassType(helper);" line.
So any help that can be provided on this issue would be amazing!
As a further related topic, my understanding is that there's no actual way to declare within LocalizationWrapper that every instance of it ever created has the TypeConverter, but instead the requirement is that in other classes/wrappers that want to use it, the following is done:
Meaning, that if I have SomeWrapper, ExtraWrapper, MoreWrapper, and they all utilize the LocalizationWrapper, I need to remember to add that ".Meta<...>(...);" bit at the end each time. Is that correct?
Thanks again to any who respond!
Greetings there, I'm attempting to write a TypeConverter in C++ that will allow me to bind a particular wrapper into Strings. There's a bit of sanity there, I promise.
Below is a sanitized/simplified form of the wrapper. In essence, life is made much easier if we have a singular wrapper which, if given a LocalizationKey stores the localized text generated from that key, but also it has the ability to have text applied that isn't supposed to be localized (ex: a user typed in a name for something). The actual functionality of this wrapper is much more than this, but this is the pertinent aspects of the wrapper for this usecase. I should further clarify, while yes, I CAN just always bind through the Wrapper into OutputText, the design requirements from the rest of my team prohibit this. (In short they want to do {Binding Group.Name} and not {Binding Group.Name.OutputText}.)
Code: Select all
class LocalizationWrapper final : public CNotifyPropertyChangedBase
{
void SetLocalizationKey( SomeKey ) { OutputText = LocalizationMagic(SomeKey); }
void SetUnlocalizedText( SomeText ) { OutputText = SomeText; }
explicit operator Noesis::String() const { return _OutputText; } // See Note 1.
Noesis::String LocalizationKey;
Noesis::String OutputText;
}
Below is the simplified example of the TypeConverter inside the header.
Code: Select all
class LocalizationWrapperTypeConverter : public Noesis::TypeConverter
{
bool CanConvertFrom( const Noesis::Type* type ) const override;
bool CanConvertTo...
bool TryConvertFrom...
bool TryConvertFromString...
bool TryConvertTo...
bool TryConvertToString...
NS_DECLARE_REFLECTION( Noesis::TypeMetaData<Noesis::TypeConverterMetaData>( "LocalizationWrapperTypeConverter", Noesis::TypeConverter ) // See Note 2.
}
When I expand out the macro, the error arises from the following line:
Code: Select all
typedef Noesis::TypeMetaData<Noesis::TypeConverterMetaData>( "LocalizationWrapperTypeConverter" ) SelfClass;
So any help that can be provided on this issue would be amazing!
As a further related topic, my understanding is that there's no actual way to declare within LocalizationWrapper that every instance of it ever created has the TypeConverter, but instead the requirement is that in other classes/wrappers that want to use it, the following is done:
Code: Select all
void OtherWrapper::AdditionalReflection( Noesis::TypeClassCreator& Helper )
{
Helper.Prop( "TextVariable", &OtherWrapper::GetTextVariable ).Meta<Noesis::TypeConverterMetaData>( "LocalizationWrapperTypeConverter" );
}
Thanks again to any who respond!
Last edited by MarcusM-PDX on 19 Aug 2024, 15:31, edited 2 times in total.
-
sfernandez
Site Admin
- Posts: 3106
- Joined:
Re: Custom TypeConverter for Wrapper to String
You are not providing the expected params to this macro NS_DECLARE_REFLECTION( Noesis::TypeMetaData... ). You should specify there your type and its base type:
In order to be automatically associated with the LocalizationWrapper type, the converter should be registered in the reflection with the following name:
Then you have to register your converter in the factory so it can be found and created by name:
At this point when a binding receives a value of type LocalizationWrapper, it can find your LocalizationWrapperTypeConverter and use it to automatically convert to string (or any type it supports).
But you can also use this converter explicitly in a property by using the TypeConverterMetaData:
Please let me know if you have further questions.
Code: Select all
NS_DECLARE_REFLECTION( LocalizationWrapperTypeConverter, Noesis::TypeConverter )
Code: Select all
NS_IMPLEMENT_REFLECTION( LocalizationWrapperTypeConverter, "Converter<LocalizationWrapper>")
Code: Select all
Noesis::RegisterComponent<LocalizationWrapperTypeConverter>();
But you can also use this converter explicitly in a property by using the TypeConverterMetaData:
Code: Select all
NS_IMPLEMENT_REFLECTION(SomeClass, "MyNamespace.SomeClass")
{
NsProp("SomeProperty", &SomeClass::GetSomeProperty, &SomeClass::SetSomeProperty).
.Meta<TypeConverterMetaData>("Converter<LocalizationWrapper>");
...
}
- MarcusM-PDX
- Posts: 7
- Joined:
Re: Custom TypeConverter for Wrapper to String
That did the trick! Thanks!
- MarcusM-PDX
- Posts: 7
- Joined:
Re: Custom TypeConverter for Wrapper to String
Here's the complete TypeConverter Example.
In this example, I have a wrapper that is enclosing various Strings but is not itself a String, and I want to be able to do a situation where I bind the Wrapper to something like a TextBox.Text field.
Below is the example LocalizationWrapper which needs the TypeConverter. I don't include the various Noesis-macros in this part because nothing interesting about this example is happening here. It's just here to show the approximate idea of the scenario.
This is for the TypeConverter's header file.
For the TypeConverter's CPP file.
As always don't forget to register it!
The above situation should automatically ensure that whenever the LocalizationWrapper is being bound to something, if necessary, it will use the TypeConverter we've written. But an alternate way to forcefully assign it if somehow it's getting overwritten/intercepted by another Converter is below.
In this example, I have a wrapper that is enclosing various Strings but is not itself a String, and I want to be able to do a situation where I bind the Wrapper to something like a TextBox.Text field.
Below is the example LocalizationWrapper which needs the TypeConverter. I don't include the various Noesis-macros in this part because nothing interesting about this example is happening here. It's just here to show the approximate idea of the scenario.
Code: Select all
class LocalizationWrapper final : CNotifyPropertyChangedBase
{
void SetLocalizationKey( SomeKey ) { OutputText = LocalizationMagic( SomeKey ); }
void SetUnlocalizedText( SomeText ) { OutputText = SomeText; }
explicit operator Noesis::String() const { return _OutputText; }
Noesis::String LocalizationKey;
Noesis::String OutputText;
// Missing Boilerplate Macros (nothing necessary for the TypeConverter's use is missing).
}
Code: Select all
class LocalizationTypeConverter : public Noesis::TypeConverter
{
public:
bool CanConvertFrom( const Noesis::Type* type ) const override;
bool CanConvertTo( const Noesis::Type* type ) const override;
bool TryConvertFrom ( BaseComponent* object, Noesis::Ptr<BaseComponent>& result ) const override;
bool TryConvertFromString( const char* str, Noesis::Ptr<BaseComponent>& result ) const override;
bool TryConvertTo( BaseComponent* object, const Noesis::Type::* type, Noesis::Ptr<BaseComponent>& result ) const override;
bool TryConvertToString( BaseComponent* object, Noesis::BaseString& result ) const override;
NS_DECLARE_REFLECTION( LocalizationTypeConverter, Noesis::TypeConverter )
}
Code: Select all
NS_IMPLEMENT_REFLECTION( LocalizationTypeConverter, "Converter<LocalizationWrapper>" ) // TAKE NOTE! The <> encloses the Type you want the TypeConverter to apply to!
{
}
bool LocalizationTypeConverter::CanConvertFrom( const Noesis::Type* type ) const
{
bool SelfCheck = ( type == Noesis::TypeOf<LocalizationWrapper>() );
bool NoesisStyleString = ( type == Noesis::TypeOf<Noesis::String>() );
return SelfCheck || NoesisStyleString;
}
bool LocalizationTypeConverter::CanConvertTo( const Noesis::Type* type ) const
{
bool SelfCheck = ( type == Noesis::TypeOf<LocalizationWrapper>() );
bool NoesisStyleString = ( type == Noesis::TypeOf<Noesis::String>() );
return SelfCheck || NoesisStyleString;
}
bool LocalizationTypeConverter::TryConvertFrom( BaseComponent* object, Noesis::Ptr<BaseComponent>& result ) const
{
if ( object == nullptr )
{
// During a new window opening, there may be a frame where Bindings are passing Null instead of actual objects.
// This might be a problem with my code specifically and is unnecessary for yours, but I figured I'd save you some diagnosing time just in case.
result = Noesis::Boxing::Box<LocalizationWrapper*>( Noesis::MakePtr<LocalizationWrapper>( "" ) );
return true;
}
else if ( Noesis::Boxing::CanUnbox<Noesis::String>( object ) )
{
const Noesis::String UnboxedString = Noesis::Boxing::Unbox<Noesis::String>( object );
result = Noesis::Boxing::Box<LocalizationWrapper*>( Noesis::MakePtr<LocalizationWrapper>( UnboxedString.Begin() ) );
return true;
}
return false;
}
bool LocalizationTypeConverter::TryConvertFromString( const char* str, Noesis::Ptr<BaseComponent>& result ) const
{
result = Noesis::MakePtr<LocalizationTypeWrapper>( str );
return true;
}
bool LocalizationTypeConverter::TryConvertTo( BaseComponent* object, const Noesis::Type* type, Noesis::Ptr<BaseComponent>& result ) const
{
if ( type == Noesis::TypeOf<Noesis::String>() )
{
const LocalizationWrapper* TempLoc = Noesis::DynamicCast<LocalizationWrapper*>( object );
result = Noesis::Boxing::Box<Noesis::String>( TempLoc->GetLocalizedText().GetCharPtr() );
return true;
}
return false;
}
bool LocalizationTypeConverter::TryConvertToString( BaseComponent* object, Noesis::BaseString& result ) const
{
result.Assign( object->ToString() );
return true;
}
Code: Select all
Noesis::RegisterComponent<LocalizationTypeConverter>();
Noesis::RegisterComponent<LocalizationWrapper>();
Code: Select all
// This part is optional!
NS_IMPLEMENT REFLECTION( SomeClass, "MyNamespace.SomeClass" )
{
NsProp( "SomeProperty", &SomeClass::GetSomeProperty, &SomeClass::SetSomeProperty ).Meta<TypeConverterMetaData>( "Converter<LocalizationWrapper>" ); // Note that this is the Wrapper type, not the TypeConverter.
}
Re: Custom TypeConverter for Wrapper to String
Thanks for sharing the example! We should definitely improve the documentation regarding how to register and user converters.
Who is online
Users browsing this forum: No registered users and 2 guests