mkclass: introduce *.ti class field attribute notify_args

If specified e.g. on T#foo_bar, then T#SetFooBar(), T#NotifyFooBar() and T::OnFooBarChanged get an additional argument of type `const NotifyFieldArgs<T::NotifyFooBarArgs>&`. That type can be customized like this:

`template<>struct NotifyFieldArgs<T::NotifyFooBarArgs>{int I=42;};`
This commit is contained in:
Alexander A. Klimov 2024-11-13 12:59:09 +01:00
parent a65f2d6b41
commit 8813e10d71
4 changed files with 101 additions and 10 deletions

View File

@ -219,6 +219,11 @@ class ObjectImpl
{ {
}; };
template<class>
struct NotifyFieldArgs
{
};
} }
#endif /* OBJECT_H */ #endif /* OBJECT_H */

View File

@ -136,6 +136,7 @@ get_virtual { yylval->num = FAGetVirtual; return T_FIELD_ATTRIBUTE; }
set_virtual { yylval->num = FASetVirtual; return T_FIELD_ATTRIBUTE; } set_virtual { yylval->num = FASetVirtual; return T_FIELD_ATTRIBUTE; }
signal_with_old_value { yylval->num = FASignalWithOldValue; return T_FIELD_ATTRIBUTE; } signal_with_old_value { yylval->num = FASignalWithOldValue; return T_FIELD_ATTRIBUTE; }
virtual { yylval->num = FAGetVirtual | FASetVirtual; return T_FIELD_ATTRIBUTE; } virtual { yylval->num = FAGetVirtual | FASetVirtual; return T_FIELD_ATTRIBUTE; }
notify_args { yylval->num = FANotifyArgs; return T_FIELD_ATTRIBUTE; }
navigation { return T_NAVIGATION; } navigation { return T_NAVIGATION; }
validator { return T_VALIDATOR; } validator { return T_VALIDATOR; }
required { return T_REQUIRED; } required { return T_REQUIRED; }

View File

@ -431,7 +431,18 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
int num = 0; int num = 0;
for (const Field& field : klass.Fields) { for (const Field& field : klass.Fields) {
m_Impl << "\t\t" << "case " << num << ":" << std::endl m_Impl << "\t\t" << "case " << num << ":" << std::endl
<< "\t\t\t" << "ObjectImpl<" << klass.Name << ">::On" << field.GetFriendlyName() << "Changed.connect(callback);" << std::endl << "\t\t\tObjectImpl<" << klass.Name << ">::On" << field.GetFriendlyName() << "Changed.connect(";
if (field.Attributes & FANotifyArgs) {
m_Impl << "[callback](const Object::Ptr& obj, const Value& val, const NotifyFieldArgs<ObjectImpl<"
<< klass.Name << ">::Notify" << field.GetFriendlyName() << "Args>&) {" << std::endl
<< "\t\t\t\tcallback(obj, val);" << std::endl
<< "\t\t\t}";
} else {
m_Impl << "callback";
}
m_Impl << ");" << std::endl
<< "\t\t\t" << "break;" << std::endl; << "\t\t\t" << "break;" << std::endl;
num++; num++;
} }
@ -823,6 +834,27 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
} }
} }
/* T for NotifyFieldArgs<T> */
for (auto& field : klass.Fields) {
if (field.Attributes & FANotifyArgs) {
m_Header << "public:" << std::endl
<< "\tstruct Notify" << field.GetFriendlyName() << "Args {};" << std::endl
<< "\tstatic const NotifyFieldArgs<Notify" << field.GetFriendlyName()
<< "Args> Notify" << field.GetFriendlyName() << "DefaultArgs;" << std::endl;
m_Impl << "/**" << std::endl
<< " * The default for the notifyArgs param of the method ObjectImpl<"
<< klass.Name << ">::Set" << field.GetFriendlyName() << "()." << std::endl
<< " * " << std::endl
<< " * This default can't be just {} (default constructor) because that triggers the following:" << std::endl
<< " * error: explicit specialization of 'icinga::NotifyFieldArgs<icinga::ObjectImpl<icinga::"
<< klass.Name << ">::Notify" << field.GetFriendlyName() << "Args>' after instantiation" << std::endl
<< " */" << std::endl
<< "const NotifyFieldArgs<ObjectImpl<" << klass.Name << ">::Notify" << field.GetFriendlyName() << "Args> ObjectImpl<"
<< klass.Name << ">::Notify" << field.GetFriendlyName() << "DefaultArgs;" << std::endl << std::endl;
}
}
/* setters */ /* setters */
for (const Field& field : klass.Fields) { for (const Field& field : klass.Fields) {
std::string prot; std::string prot;
@ -838,15 +870,29 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
if (field.Attributes & FASetVirtual || field.PureSetAccessor) if (field.Attributes & FASetVirtual || field.PureSetAccessor)
m_Header << "virtual "; m_Header << "virtual ";
m_Header << "void Set" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " value, bool suppress_events = false, const Value& cookie = Empty)"; m_Header << "void Set" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType()
<< " value, bool suppress_events = false, const Value& cookie = Empty";
if (field.Attributes & FANotifyArgs) {
m_Header << ", const NotifyFieldArgs<Notify" << field.GetFriendlyName()
<< "Args>& notifyArgs = Notify" << field.GetFriendlyName() << "DefaultArgs";
}
m_Header << ")";
if (field.PureSetAccessor) { if (field.PureSetAccessor) {
m_Header << " = 0;" << std::endl; m_Header << " = 0;" << std::endl;
} else { } else {
m_Header << ";" << std::endl; m_Header << ";" << std::endl;
m_Impl << "void ObjectImpl<" << klass.Name << ">::Set" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " value, bool suppress_events, const Value& cookie)" << std::endl m_Impl << "void ObjectImpl<" << klass.Name << ">::Set" << field.GetFriendlyName() << "("
<< "{" << std::endl; << field.Type.GetArgumentType() << " value, bool suppress_events, const Value& cookie";
if (field.Attributes & FANotifyArgs) {
m_Impl << ", const NotifyFieldArgs<Notify" << field.GetFriendlyName() << "Args>& notifyArgs";
}
m_Impl << ")" << std::endl << "{" << std::endl;
if (field.Type.IsName || !field.TrackAccessor.empty() || field.Attributes & FASignalWithOldValue) if (field.Type.IsName || !field.TrackAccessor.empty() || field.Attributes & FASignalWithOldValue)
m_Impl << "\t" << "Value oldValue = Get" << field.GetFriendlyName() << "();" << std::endl m_Impl << "\t" << "Value oldValue = Get" << field.GetFriendlyName() << "();" << std::endl
@ -867,7 +913,13 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
} }
m_Impl << "\t" << "if (!suppress_events) {" << std::endl m_Impl << "\t" << "if (!suppress_events) {" << std::endl
<< "\t\t" << "Notify" << field.GetFriendlyName() << "(cookie);" << std::endl; << "\t\tNotify" << field.GetFriendlyName() << "(cookie";
if (field.Attributes & FANotifyArgs) {
m_Impl << ", notifyArgs";
}
m_Impl << ");" << std::endl;
if (field.Attributes & FASignalWithOldValue) { if (field.Attributes & FASignalWithOldValue) {
m_Impl << "\t\t" << "if (!dobj || dobj->IsActive())" << std::endl m_Impl << "\t\t" << "if (!dobj || dobj->IsActive())" << std::endl
@ -1022,9 +1074,22 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
prot = "public"; prot = "public";
m_Header << prot << ":" << std::endl m_Header << prot << ":" << std::endl
<< "\t" << "virtual void Notify" << field.GetFriendlyName() << "(const Value& cookie = Empty);" << std::endl; << "\tvirtual void Notify" << field.GetFriendlyName() << "(const Value& cookie = Empty";
m_Impl << "void ObjectImpl<" << klass.Name << ">::Notify" << field.GetFriendlyName() << "(const Value& cookie)" << std::endl if (field.Attributes & FANotifyArgs) {
m_Header << ", const NotifyFieldArgs<Notify" << field.GetFriendlyName()
<< "Args>& notifyArgs = Notify" << field.GetFriendlyName() << "DefaultArgs";
}
m_Header << ");" << std::endl;
m_Impl << "void ObjectImpl<" << klass.Name << ">::Notify" << field.GetFriendlyName() << "(const Value& cookie";
if (field.Attributes & FANotifyArgs) {
m_Impl << ", const NotifyFieldArgs<Notify" << field.GetFriendlyName() << "Args>& notifyArgs";
}
m_Impl << ")" << std::endl
<< "{" << std::endl; << "{" << std::endl;
if (field.Name != "active") { if (field.Name != "active") {
@ -1033,7 +1098,13 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
<< "\t"; << "\t";
} }
m_Impl << "\t" << "On" << field.GetFriendlyName() << "Changed(static_cast<" << klass.Name << " *>(this), cookie);" << std::endl m_Impl << "\tOn" << field.GetFriendlyName() << "Changed(static_cast<" << klass.Name << " *>(this), cookie";
if (field.Attributes & FANotifyArgs) {
m_Impl << ", notifyArgs";
}
m_Impl << ");" << std::endl
<< "}" << std::endl << std::endl; << "}" << std::endl << std::endl;
} }
@ -1075,8 +1146,21 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
m_Header << "public:" << std::endl; m_Header << "public:" << std::endl;
for (const Field& field : klass.Fields) { for (const Field& field : klass.Fields) {
m_Header << "\t" << "static boost::signals2::signal<void (const intrusive_ptr<" << klass.Name << ">&, const Value&)> On" << field.GetFriendlyName() << "Changed;" << std::endl; m_Header << "\tstatic boost::signals2::signal<void(const intrusive_ptr<" << klass.Name << ">&, const Value&";
m_Impl << std::endl << "boost::signals2::signal<void (const intrusive_ptr<" << klass.Name << ">&, const Value&)> ObjectImpl<" << klass.Name << ">::On" << field.GetFriendlyName() << "Changed;" << std::endl << std::endl;
if (field.Attributes & FANotifyArgs) {
m_Header << ", const NotifyFieldArgs<Notify" << field.GetFriendlyName() << "Args>&";
}
m_Header << ")> On" << field.GetFriendlyName() << "Changed;" << std::endl;
m_Impl << std::endl << "boost::signals2::signal<void(const intrusive_ptr<" << klass.Name << ">&, const Value&";
if (field.Attributes & FANotifyArgs) {
m_Impl << ", const NotifyFieldArgs<ObjectImpl<" << klass.Name << ">::Notify" << field.GetFriendlyName() << "Args>&";
}
m_Impl << ")> ObjectImpl<" << klass.Name << ">::On" << field.GetFriendlyName() << "Changed;" << std::endl << std::endl;
if (field.Attributes & FASignalWithOldValue) { if (field.Attributes & FASignalWithOldValue) {
m_Header << "\t" << "static boost::signals2::signal<void (const intrusive_ptr<" << klass.Name m_Header << "\t" << "static boost::signals2::signal<void (const intrusive_ptr<" << klass.Name

View File

@ -62,6 +62,7 @@ enum FieldAttribute
FASetVirtual = 16384, FASetVirtual = 16384,
FAActivationPriority = 32768, FAActivationPriority = 32768,
FASignalWithOldValue = 65536, FASignalWithOldValue = 65536,
FANotifyArgs = 131072,
}; };
struct FieldType struct FieldType