Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Display/Menu.cpp9
-rw-r--r--src/Display/MenuItem.cpp2
-rw-r--r--src/GCodes/GCodeBuffer/StringParser.cpp185
-rw-r--r--src/GCodes/GCodeBuffer/StringParser.h1
-rw-r--r--src/GCodes/GCodes.cpp4
-rw-r--r--src/GCodes/GCodes.h2
-rw-r--r--src/GCodes/GCodes2.cpp2
-rw-r--r--src/GCodes/GCodes3.cpp14
-rw-r--r--src/Heating/Heat.cpp104
-rw-r--r--src/Networking/ESP8266WiFi/WiFiInterface.cpp5
-rw-r--r--src/ObjectModel/ObjectModel.h54
11 files changed, 263 insertions, 119 deletions
diff --git a/src/Display/Menu.cpp b/src/Display/Menu.cpp
index 76098d06..05954849 100644
--- a/src/Display/Menu.cpp
+++ b/src/Display/Menu.cpp
@@ -540,13 +540,12 @@ void Menu::EncoderActionEnterItemHelper()
if (highlightedItem->Select(command.GetRef()))
{
char *pcCurrentCommand = command.GetRef().Pointer();
- int nNextCommandIndex = StringContains(pcCurrentCommand, "|");
- while (-1 != nNextCommandIndex)
+ char *pcNextCommand;
+ while ((pcNextCommand = strchr(pcCurrentCommand, '|')) != nullptr)
{
- *(pcCurrentCommand + nNextCommandIndex) = '\0';
+ *pcNextCommand = '\0';
EncoderAction_ExecuteHelper(pcCurrentCommand);
- pcCurrentCommand += nNextCommandIndex + 1;
- nNextCommandIndex = StringContains(pcCurrentCommand, "|");
+ pcCurrentCommand = pcNextCommand + 1;
}
EncoderAction_ExecuteHelper(pcCurrentCommand);
}
diff --git a/src/Display/MenuItem.cpp b/src/Display/MenuItem.cpp
index 3fa0d3a0..0f807d13 100644
--- a/src/Display/MenuItem.cpp
+++ b/src/Display/MenuItem.cpp
@@ -1056,7 +1056,7 @@ bool FilesMenuItem::Select(const StringRef& cmd)
// TODO: do this on the way in and it might be less work...
// On the other hand, this only occurs when an item is selected so it's O(1) vs. O(n)
- nReplacementIndex = StringContains(cmd.c_str(), "menu");
+ nReplacementIndex = cmd.Contains("menu");
if (nReplacementIndex != -1)
{
cmd.Truncate(nReplacementIndex);
diff --git a/src/GCodes/GCodeBuffer/StringParser.cpp b/src/GCodes/GCodeBuffer/StringParser.cpp
index bceefe53..8f8329fe 100644
--- a/src/GCodes/GCodeBuffer/StringParser.cpp
+++ b/src/GCodes/GCodeBuffer/StringParser.cpp
@@ -1809,6 +1809,24 @@ void StringParser::BalanceTypes(ExpressionValue& val1, ExpressionValue& val2)
}
}
+void StringParser::EnsureNumeric(ExpressionValue& val)
+{
+ switch (val.type)
+ {
+ case TYPE_OF(uint32_t):
+ val.type = TYPE_OF(int32_t);
+ val.iVal = val.uVal;
+ break;
+
+ case TYPE_OF(int32_t):
+ case TYPE_OF(float):
+ break;
+
+ default:
+ throw ConstructParseException("expected numeric operand");
+ }
+}
+
void StringParser::ConvertToFloat(ExpressionValue& val)
{
switch (val.type)
@@ -1943,18 +1961,9 @@ ExpressionValue StringParser::ParseNumber()
ExpressionValue StringParser::ParseIdentifier()
{
unsigned int start = readPointer;
- unsigned int numBrackets = 0;
char c;
- while (isalpha((c = gb.buffer[readPointer])) || isdigit(c) || c == '_' || c == '.' || c == '(' || (c == ')' && numBrackets != 0))
+ while (isalpha((c = gb.buffer[readPointer])) || isdigit(c) || c == '_' || c == '.')
{
- if (c == '(')
- {
- ++numBrackets;
- }
- else if (c == ')')
- {
- --numBrackets;
- }
++readPointer;
}
String<MaxVariableNameLength> varName;
@@ -1963,14 +1972,158 @@ ExpressionValue StringParser::ParseIdentifier()
throw ConstructParseException("variable name too long");;
}
- //TODO consider supporting standard CNC functions here, also 'true', 'false', 'pi'
- const ExpressionValue val = reprap.GetObjectValue(*this, varName.c_str());
- if (c != '}')
+ // Check for the names of constants
+ if (varName.Equals("true"))
{
- throw ConstructParseException("expected '}'");
+ return ExpressionValue(true);
}
- ++readPointer;
- return val;
+
+ if (varName.Equals("false"))
+ {
+ return ExpressionValue(false);
+ }
+
+ if (varName.Equals("pi"))
+ {
+ return ExpressionValue(Pi);
+ }
+
+ // Check whether it is a function call
+ SkipWhiteSpace();
+ if (gb.buffer[readPointer] == '(')
+ {
+ // It's a function call
+ ExpressionValue rslt = ParseExpression(0);
+ if (varName.Equals("abs"))
+ {
+ switch (rslt.type)
+ {
+ case TYPE_OF(int32_t):
+ rslt.iVal = labs(rslt.iVal);
+ break;
+
+ case TYPE_OF(float):
+ rslt.fVal = fabsf(rslt.fVal);
+ break;
+
+ default:
+ throw ConstructParseException("expected numeric operand");
+ }
+ }
+ else if (varName.Equals("sin"))
+ {
+ ConvertToFloat(rslt);
+ rslt.fVal = sinf(rslt.fVal);
+ }
+ else if (varName.Equals("cos"))
+ {
+ ConvertToFloat(rslt);
+ rslt.fVal = cosf(rslt.fVal);
+ }
+ else if (varName.Equals("tan"))
+ {
+ ConvertToFloat(rslt);
+ rslt.fVal = tanf(rslt.fVal);
+ }
+ else if (varName.Equals("asin"))
+ {
+ ConvertToFloat(rslt);
+ rslt.fVal = asinf(rslt.fVal);
+ }
+ else if (varName.Equals("acos"))
+ {
+ ConvertToFloat(rslt);
+ rslt.fVal = acosf(rslt.fVal);
+ }
+ else if (varName.Equals("atan"))
+ {
+ ConvertToFloat(rslt);
+ rslt.fVal = atanf(rslt.fVal);
+ }
+ else if (varName.Equals("atan2"))
+ {
+ ConvertToFloat(rslt);
+ SkipWhiteSpace();
+ if (gb.buffer[readPointer] != ',')
+ {
+ throw ConstructParseException("expected ','");
+ }
+ ++readPointer;
+ SkipWhiteSpace();
+ ExpressionValue nextOperand = ParseExpression(0);
+ ConvertToFloat(nextOperand);
+ rslt.fVal = atan2f(rslt.fVal, nextOperand.fVal);
+ }
+ else if (varName.Equals("sqrt"))
+ {
+ ConvertToFloat(rslt);
+ rslt.fVal = sqrtf(rslt.fVal);
+ }
+ else if (varName.Equals("isnan"))
+ {
+ ConvertToFloat(rslt);
+ rslt.type = TYPE_OF(bool);
+ rslt.bVal = (isnan(rslt.fVal) != 0);
+ }
+ else if (varName.Equals("max"))
+ {
+ for (;;)
+ {
+ SkipWhiteSpace();
+ if (gb.buffer[readPointer] != ',')
+ {
+ break;
+ }
+ ++readPointer;
+ SkipWhiteSpace();
+ ExpressionValue nextOperand = ParseExpression(0);
+ BalanceNumericTypes(rslt, nextOperand);
+ if (rslt.type == TYPE_OF(float))
+ {
+ rslt.fVal = max<float>(rslt.fVal, nextOperand.fVal);
+ }
+ else
+ {
+ rslt.iVal = max<int32_t>(rslt.iVal, nextOperand.iVal);
+ }
+ }
+ }
+ else if (varName.Equals("min"))
+ {
+ for (;;)
+ {
+ SkipWhiteSpace();
+ if (gb.buffer[readPointer] != ',')
+ {
+ break;
+ }
+ ++readPointer;
+ SkipWhiteSpace();
+ ExpressionValue nextOperand = ParseExpression(0);
+ BalanceNumericTypes(rslt, nextOperand);
+ if (rslt.type == TYPE_OF(float))
+ {
+ rslt.fVal = min<float>(rslt.fVal, nextOperand.fVal);
+ }
+ else
+ {
+ rslt.iVal = min<int32_t>(rslt.iVal, nextOperand.iVal);
+ }
+ }
+ }
+ else
+ {
+ throw ConstructParseException("unknown function");
+ }
+ SkipWhiteSpace();
+ if (gb.buffer[readPointer] != ')')
+ {
+ throw ConstructParseException("expected ')'");
+ }
+ return rslt;
+ }
+
+ return reprap.GetObjectValue(*this, varName.c_str());
}
ParseException StringParser::ConstructParseException(const char *str) const
diff --git a/src/GCodes/GCodeBuffer/StringParser.h b/src/GCodes/GCodeBuffer/StringParser.h
index c4c39687..7eadbe1a 100644
--- a/src/GCodes/GCodeBuffer/StringParser.h
+++ b/src/GCodes/GCodeBuffer/StringParser.h
@@ -122,6 +122,7 @@ private:
void BalanceTypes(ExpressionValue& val1, ExpressionValue& val2) THROWS_PARSE_ERROR;
void ConvertToFloat(ExpressionValue& val) THROWS_PARSE_ERROR;
void ConvertToBool(ExpressionValue& val) THROWS_PARSE_ERROR;
+ void EnsureNumeric(ExpressionValue& val) THROWS_PARSE_ERROR;
void SkipWhiteSpace();
diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp
index 40180f0f..8f9fba89 100644
--- a/src/GCodes/GCodes.cpp
+++ b/src/GCodes/GCodes.cpp
@@ -3487,13 +3487,13 @@ GCodeResult GCodes::LoadFilament(GCodeBuffer& gb, const StringRef& reply)
String<FilamentNameLength> filamentName;
gb.GetQuotedString(filamentName.GetRef());
- if (StringContains(filamentName.c_str(), ",") >= 0)
+ if (filamentName.Contains(',') >= 0)
{
reply.copy("Filament names must not contain commas");
return GCodeResult::error;
}
- if (StringEqualsIgnoreCase(filamentName.c_str(), tool->GetFilament()->GetName()))
+ if (filamentName.EqualsIgnoreCase(tool->GetFilament()->GetName()))
{
// Filament already loaded - nothing to do
return GCodeResult::ok;
diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h
index 997ffd9e..aadb2da5 100644
--- a/src/GCodes/GCodes.h
+++ b/src/GCodes/GCodes.h
@@ -259,7 +259,7 @@ private:
GCodeResult StraightProbe(GCodeBuffer& gb, const StringRef& reply); // Deal with a G38.x
GCodeResult DoDriveMapping(GCodeBuffer& gb, const StringRef& reply); // Deal with a M584
GCodeResult ProbeTool(GCodeBuffer& gb, const StringRef& reply); // Deal with a M585
- GCodeResult FindCenterOfCavity(GCodeBuffer& gb, const StringRef& reply, const bool towardsMin = true); // Deal with a M675
+ GCodeResult FindCenterOfCavity(GCodeBuffer& gb, const StringRef& reply, const bool towardsMin = true) THROWS_PARSE_ERROR; // Deal with a M675
GCodeResult SetDateTime(GCodeBuffer& gb,const StringRef& reply); // Deal with a M905
GCodeResult SavePosition(GCodeBuffer& gb,const StringRef& reply); // Deal with G60
GCodeResult ConfigureDriver(GCodeBuffer& gb,const StringRef& reply); // Deal with M569
diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp
index 95169b7d..8d7073bc 100644
--- a/src/GCodes/GCodes2.cpp
+++ b/src/GCodes/GCodes2.cpp
@@ -4358,7 +4358,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply)
{
String<StringLength20> eraseString;
gb.GetQuotedString(eraseString.GetRef());
- if (strcmp(eraseString.c_str(), "ERASE") == 0)
+ if (eraseString.Equals("ERASE"))
{
reason = (uint16_t)SoftwareResetReason::erase;
}
diff --git a/src/GCodes/GCodes3.cpp b/src/GCodes/GCodes3.cpp
index 43e52665..3ffeef1e 100644
--- a/src/GCodes/GCodes3.cpp
+++ b/src/GCodes/GCodes3.cpp
@@ -898,17 +898,9 @@ GCodeResult GCodes::FindCenterOfCavity(GCodeBuffer& gb, const StringRef& reply,
moveBuffer.coords[axis] = towardsMin ? platform.AxisMinimum(axis) : platform.AxisMaximum(axis);
// Deal with feed rate
- if (gb.Seen(feedrateLetter))
- {
- const float rate = gb.GetDistance();
- gb.MachineState().feedRate = rate * SecondsToMinutes; // don't apply the speed factor to homing and other special moves
- }
- else
- {
- reply.copy("No feed rate provided.");
- return GCodeResult::badOrMissingParameter;
- }
- moveBuffer.feedRate = gb.MachineState().feedRate;
+ gb.MustSee(feedrateLetter);
+ const float rate = gb.GetDistance();
+ moveBuffer.feedRate = gb.MachineState().feedRate = rate * SecondsToMinutes; // don't apply the speed factor to homing and other special moves
const bool probeOk = (useProbe)
? platform.GetEndstops().EnableZProbe(probeNumberToUse)
diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp
index 5d436de5..76616120 100644
--- a/src/Heating/Heat.cpp
+++ b/src/Heating/Heat.cpp
@@ -130,71 +130,67 @@ ReadLockedPointer<Heater> Heat::FindHeater(int heater) const noexcept
// Process M307
GCodeResult Heat::SetOrReportHeaterModel(GCodeBuffer& gb, const StringRef& reply)
{
- if (gb.Seen('H'))
+ gb.MustSee('H');
+ const unsigned int heater = gb.GetUIValue();
+ const auto h = FindHeater(heater);
+ if (h.IsNotNull())
{
- const unsigned int heater = gb.GetUIValue();
- const auto h = FindHeater(heater);
- if (h.IsNotNull())
+ const FopDt& model = h->GetModel();
+ bool seen = false;
+ float gain = model.GetGain(),
+ tc = model.GetTimeConstant(),
+ td = model.GetDeadTime(),
+ maxPwm = model.GetMaxPwm(),
+ voltage = model.GetVoltage();
+ int32_t dontUsePid = model.UsePid() ? 0 : 1;
+ int32_t inversionParameter = 0;
+
+ gb.TryGetFValue('A', gain, seen);
+ gb.TryGetFValue('C', tc, seen);
+ gb.TryGetFValue('D', td, seen);
+ gb.TryGetIValue('B', dontUsePid, seen);
+ gb.TryGetFValue('S', maxPwm, seen);
+ gb.TryGetFValue('V', voltage, seen);
+ gb.TryGetIValue('I', inversionParameter, seen);
+
+ if (seen)
{
- const FopDt& model = h->GetModel();
- bool seen = false;
- float gain = model.GetGain(),
- tc = model.GetTimeConstant(),
- td = model.GetDeadTime(),
- maxPwm = model.GetMaxPwm(),
- voltage = model.GetVoltage();
- int32_t dontUsePid = model.UsePid() ? 0 : 1;
- int32_t inversionParameter = 0;
-
- gb.TryGetFValue('A', gain, seen);
- gb.TryGetFValue('C', tc, seen);
- gb.TryGetFValue('D', td, seen);
- gb.TryGetIValue('B', dontUsePid, seen);
- gb.TryGetFValue('S', maxPwm, seen);
- gb.TryGetFValue('V', voltage, seen);
- gb.TryGetIValue('I', inversionParameter, seen);
-
- if (seen)
+ const bool inverseTemperatureControl = (inversionParameter == 1 || inversionParameter == 3);
+ const GCodeResult rslt = h->SetModel(gain, tc, td, maxPwm, voltage, dontUsePid == 0, inverseTemperatureControl, reply);
+ if (rslt != GCodeResult::ok)
{
- const bool inverseTemperatureControl = (inversionParameter == 1 || inversionParameter == 3);
- const GCodeResult rslt = h->SetModel(gain, tc, td, maxPwm, voltage, dontUsePid == 0, inverseTemperatureControl, reply);
- if (rslt != GCodeResult::ok)
- {
- return rslt;
- }
+ return rslt;
}
- else if (!model.IsEnabled())
+ }
+ else if (!model.IsEnabled())
+ {
+ reply.printf("Heater %u is disabled", heater);
+ }
+ else
+ {
+ const char* const mode = (!model.UsePid()) ? "bang-bang"
+ : (model.ArePidParametersOverridden()) ? "custom PID"
+ : "PID";
+ reply.printf("Heater %u model: gain %.1f, time constant %.1f, dead time %.1f, max PWM %.2f, calibration voltage %.1f, mode %s", heater,
+ (double)model.GetGain(), (double)model.GetTimeConstant(), (double)model.GetDeadTime(), (double)model.GetMaxPwm(), (double)model.GetVoltage(), mode);
+ if (model.IsInverted())
{
- reply.printf("Heater %u is disabled", heater);
+ reply.cat(", inverted temperature control");
}
- else
+ if (model.UsePid())
{
- const char* const mode = (!model.UsePid()) ? "bang-bang"
- : (model.ArePidParametersOverridden()) ? "custom PID"
- : "PID";
- reply.printf("Heater %u model: gain %.1f, time constant %.1f, dead time %.1f, max PWM %.2f, calibration voltage %.1f, mode %s", heater,
- (double)model.GetGain(), (double)model.GetTimeConstant(), (double)model.GetDeadTime(), (double)model.GetMaxPwm(), (double)model.GetVoltage(), mode);
- if (model.IsInverted())
- {
- reply.cat(", inverted temperature control");
- }
- if (model.UsePid())
- {
- // When reporting the PID parameters, we scale them by 255 for compatibility with older firmware and other firmware
- M301PidParameters params = model.GetM301PidParameters(false);
- reply.catf("\nComputed PID parameters for setpoint change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD);
- params = model.GetM301PidParameters(true);
- reply.catf("\nComputed PID parameters for load change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD);
- }
+ // When reporting the PID parameters, we scale them by 255 for compatibility with older firmware and other firmware
+ M301PidParameters params = model.GetM301PidParameters(false);
+ reply.catf("\nComputed PID parameters for setpoint change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD);
+ params = model.GetM301PidParameters(true);
+ reply.catf("\nComputed PID parameters for load change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD);
}
- return GCodeResult::ok;
}
-
- reply.printf("Heater %u not found", heater);
- return GCodeResult::error;
+ return GCodeResult::ok;
}
- return GCodeResult::badOrMissingParameter;
+ reply.printf("Heater %u not found", heater);
+ return GCodeResult::error;
}
// Process M301 or M304. 'heater' is the default heater number to use.
diff --git a/src/Networking/ESP8266WiFi/WiFiInterface.cpp b/src/Networking/ESP8266WiFi/WiFiInterface.cpp
index 04ffe795..4e68acc5 100644
--- a/src/Networking/ESP8266WiFi/WiFiInterface.cpp
+++ b/src/Networking/ESP8266WiFi/WiFiInterface.cpp
@@ -973,8 +973,8 @@ GCodeResult WiFiInterface::HandleWiFiCode(int mcode, GCodeBuffer &gb, const Stri
return GCodeResult::error;
case 588: // Forget WiFi network
- if (gb.Seen('S'))
{
+ gb.MustSee('S');
String<SsidLength> ssidText;
gb.GetQuotedString(ssidText.GetRef());
if (strcmp(ssidText.c_str(), "*") == 0)
@@ -1001,9 +1001,6 @@ GCodeResult WiFiInterface::HandleWiFiCode(int mcode, GCodeBuffer &gb, const Stri
return GCodeResult::error;
}
- reply.copy("Bad or missing parameter");
- return GCodeResult::error;
-
case 589: // Configure access point
if (gb.Seen('S'))
{
diff --git a/src/ObjectModel/ObjectModel.h b/src/ObjectModel/ObjectModel.h
index 702296bc..1d04aadc 100644
--- a/src/ObjectModel/ObjectModel.h
+++ b/src/ObjectModel/ObjectModel.h
@@ -20,6 +20,31 @@ typedef uint8_t TypeCode;
constexpr TypeCode IsArray = 128; // this is or'ed in to a type code to indicate an array
constexpr TypeCode NoType = 0; // code for an invalid or unknown type
+// Dummy types, used to define type codes
+class Bitmap32;
+class Enum32;
+class Float2; // float printed to 2 decimal places instead of 1
+class Float3; // float printed to 3 decimal places instead of 1
+class ObjectModel; // forward declaration
+
+// Function template used to get constexpr type IDs
+// Each type must return a unique type code in the range 1 to 127
+template<class T> constexpr TypeCode TypeOf();
+
+template<> constexpr TypeCode TypeOf<bool> () { return 1; }
+template<> constexpr TypeCode TypeOf<uint32_t> () { return 2; }
+template<> constexpr TypeCode TypeOf<int32_t>() { return 3; }
+template<> constexpr TypeCode TypeOf<float>() { return 4; }
+template<> constexpr TypeCode TypeOf<Float2>() { return 5; }
+template<> constexpr TypeCode TypeOf<Float3>() { return 6; }
+template<> constexpr TypeCode TypeOf<Bitmap32>() { return 7; }
+template<> constexpr TypeCode TypeOf<Enum32>() { return 8; }
+template<> constexpr TypeCode TypeOf<ObjectModel>() { return 9; }
+template<> constexpr TypeCode TypeOf<const char *>() { return 10; }
+template<> constexpr TypeCode TypeOf<IPAddress>() { return 11; }
+
+#define TYPE_OF(_t) (TypeOf<_t>())
+
// Forward declarations
class ObjectModelTableEntry;
class ObjectModel;
@@ -37,13 +62,12 @@ struct ExpressionValue
const char *sVal;
const ObjectModel *omVal;
};
-};
-// Dummy types, used to define type codes
-class Bitmap32;
-class Enum32;
-class Float2; // float printed to 2 decimal places instead of 1
-class Float3; // float printed to 3 decimal places instead of 1
+ constexpr ExpressionValue(bool b) : type(TYPE_OF(bool)), bVal(b) { }
+ constexpr ExpressionValue(float f) : type(TYPE_OF(float)), fVal(f) { }
+ constexpr ExpressionValue(int32_t i) : type(TYPE_OF(int32_t)), iVal(i) { }
+ ExpressionValue() : type(NoType) { }
+};
class ObjectModel
{
@@ -89,24 +113,6 @@ private:
uint32_t *GetBitmapObjectPointer(const char *idString);
};
-// Function template used to get constexpr type IDs
-// Each type must return a unique type code in the range 1 to 127
-template<class T> constexpr TypeCode TypeOf();
-
-template<> constexpr TypeCode TypeOf<bool> () { return 1; }
-template<> constexpr TypeCode TypeOf<uint32_t> () { return 2; }
-template<> constexpr TypeCode TypeOf<int32_t>() { return 3; }
-template<> constexpr TypeCode TypeOf<float>() { return 4; }
-template<> constexpr TypeCode TypeOf<Float2>() { return 5; }
-template<> constexpr TypeCode TypeOf<Float3>() { return 6; }
-template<> constexpr TypeCode TypeOf<Bitmap32>() { return 7; }
-template<> constexpr TypeCode TypeOf<Enum32>() { return 8; }
-template<> constexpr TypeCode TypeOf<ObjectModel>() { return 9; }
-template<> constexpr TypeCode TypeOf<const char *>() { return 10; }
-template<> constexpr TypeCode TypeOf<IPAddress>() { return 11; }
-
-#define TYPE_OF(_t) (TypeOf<_t>())
-
// Entry to describe an array
class ObjectModelArrayDescriptor
{