Skip to content

Commit 7fa4a9a

Browse files
authored
Implemented attribute setters (#15)
``` builder.element<Example>("example") .setter("value", &Example::setValue); ```
1 parent 7b2ea35 commit 7fa4a9a

File tree

2 files changed

+120
-5
lines changed

2 files changed

+120
-5
lines changed

src/imvue_element.h

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,6 @@ namespace ImVue {
754754

755755
if(!success && required) {
756756
IMVUE_EXCEPTION(ElementError, "failed to read required attribute %s", attribute);
757-
return;
758757
}
759758
}
760759

@@ -768,6 +767,93 @@ namespace ImVue {
768767
D C::* mMemPtr;
769768
};
770769

770+
/**
771+
* Setter implementation of attribute reader
772+
*/
773+
template<class C, typename Type>
774+
class AttributeSetter : public Attribute {
775+
public:
776+
777+
typedef void(C::*SetterFunc)(Type);
778+
779+
AttributeSetter(SetterFunc func, bool required = false)
780+
: Attribute(required)
781+
, mSetter(func)
782+
{
783+
static_assert(std::is_trivially_constructible<Type>::value, "setter can only be used with trivially constructible types");
784+
}
785+
786+
void read(const char* attribute, const char* str, Element* element, ScriptState* scriptState, int flags = 0, ScriptState::Fields* fields = 0) const
787+
{
788+
Type value;
789+
bool success = false;
790+
if(scriptState) {
791+
if(flags & SCRIPT) {
792+
Object object = scriptState->getObject(str, fields, element->getContext());
793+
if(!object.valid()) {
794+
IMVUE_EXCEPTION(ScriptError, "failed to evaluate data %s", str);
795+
return;
796+
}
797+
success = detail::read(object, &value);
798+
} else if(flags & TEMPLATED_STRING) {
799+
std::string retval;
800+
std::stringstream result;
801+
std::stringstream ss;
802+
bool evaluation = false;
803+
804+
for(int i = 0;;i++) {
805+
if(str[i] == '\0') {
806+
break;
807+
}
808+
809+
if(evaluation && std::strncmp(&str[i], "}}", 2) == 0) {
810+
Object object = scriptState->getObject(&ss.str()[0], fields, element->getContext());
811+
ss = std::stringstream();
812+
evaluation = false;
813+
result << object.as<ImString>().c_str();
814+
i++;
815+
continue;
816+
}
817+
818+
if(!evaluation && std::strncmp(&str[i], "{{", 2) == 0) {
819+
evaluation = true;
820+
i++;
821+
continue;
822+
}
823+
824+
if(evaluation) {
825+
ss << str[i];
826+
} else {
827+
result << str[i];
828+
}
829+
}
830+
831+
success = detail::read(&result.str()[0], &value);
832+
}
833+
}
834+
835+
if(!success) {
836+
success = detail::read(str, &value);
837+
}
838+
839+
if(success) {
840+
(static_cast<C*>(element)->*mSetter)(value);
841+
} else if(required) {
842+
IMVUE_EXCEPTION(ElementError, "failed to read required attribute %s", attribute);
843+
}
844+
}
845+
846+
bool copy(Element* element, Object& dest) const
847+
{
848+
(void)element;
849+
(void)dest;
850+
return false;
851+
}
852+
853+
private:
854+
SetterFunc mSetter;
855+
};
856+
771857
/**
772858
* Handler factory interface
773859
*/
@@ -899,23 +985,42 @@ namespace ImVue {
899985
}
900986

901987
/**
902-
* Register attribute reader
988+
* Register attribute
903989
*
904990
* @param name attribute name
905991
* @param memPtr destination to read into (&Element::variable)
906992
* @param required property is required
907993
*/
908-
template<class D>
909-
ElementBuilderImpl<C>& attribute(const char* name, D C::* memPtr, bool required = false)
994+
template<class Type>
995+
ElementBuilderImpl<C>& attribute(const char* name, Type C::* memPtr, bool required = false)
910996
{
911-
mAttributes[name] = new AttributeMemPtr<C, D>(memPtr, required);
997+
mAttributes[name] = new AttributeMemPtr<C, Type>(memPtr, required);
912998
mAttributes[name]->owner = this;
913999
if(required) {
9141000
mRequiredAttrs.push_back(name);
9151001
}
9161002
return *this;
9171003
}
9181004

1005+
/**
1006+
* Register attribute setter
1007+
*
1008+
* @param name attribute name
1009+
* @param func setter function to use (&Element::setValue)
1010+
* @param required property is required
1011+
*/
1012+
template<class Type>
1013+
ElementBuilderImpl<C>& setter(const char* name, void(C::*func)(Type), bool required = false)
1014+
{
1015+
mAttributes[name] = new AttributeSetter<C, Type>(func, required);
1016+
mAttributes[name]->owner = this;
1017+
if(required) {
1018+
mRequiredAttrs.push_back(name);
1019+
}
1020+
return *this;
1021+
}
1022+
1023+
9191024
/**
9201025
* Register text reader
9211026
*

tests/unit/parser.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ class Validator : public ImVue::Element
120120
{
121121
}
122122

123+
void useSetter(int value)
124+
{
125+
fromSetter = value;
126+
}
127+
123128
bool flag;
124129
float fl;
125130
double dbl;
@@ -134,6 +139,7 @@ class Validator : public ImVue::Element
134139
char* ch;
135140
int arrFixed[3];
136141
int* arrVariadic;
142+
int fromSetter;
137143
#if defined(WITH_LUA)
138144
ImVue::Object object;
139145
#endif
@@ -179,6 +185,7 @@ TEST_P(ReadTypesTest, ParseTypes)
179185
.attribute("vec4", &Validator::vec4, true)
180186
.attribute("arr-fixed", &Validator::arrFixed, true)
181187
.attribute("arr-variadic", &Validator::arrVariadic, true)
188+
.setter("setter", &Validator::useSetter, true)
182189
#if defined(WITH_LUA)
183190
.attribute("object", &Validator::object)
184191
#endif
@@ -205,6 +212,7 @@ TEST_P(ReadTypesTest, ParseTypes)
205212
EXPECT_EQ(el->i16, -32765);
206213
EXPECT_EQ(el->i32, -65536);
207214
EXPECT_EQ(el->i64, -1073741823);
215+
EXPECT_EQ(el->fromSetter, 100);
208216
EXPECT_FLOAT_EQ(el->vec2.x, 2);
209217
EXPECT_FLOAT_EQ(el->vec2.y, 2);
210218

@@ -242,6 +250,7 @@ const char* dataStatic = "<template>"
242250
"float='0.1' double='0.05' "
243251
"ch='hello' "
244252
"arr-fixed='{1,2,3}' arr-variadic='{1,2,3,4,5}' "
253+
"setter='100' "
245254
"/>"
246255
"</template>";
247256

@@ -255,6 +264,7 @@ const char* dataBind = "<template>"
255264
":arr-fixed='{1,2,3}' :arr-variadic='{1,2,3,4,5}' "
256265
":float='0.1' :double='0.05' "
257266
":object='{it=\"works\"}'"
267+
":setter='100' "
258268
"/>"
259269
"</template>"
260270
"<script>"

0 commit comments

Comments
 (0)