Skip to content

Commit

Permalink
Implement valid/in-enum for C++98, fix MSVC compat for C++11
Browse files Browse the repository at this point in the history
Fixes the ValidFailInEnum test for C++98.

Should also fix EnumIntRangeU and EnumLongRange{S,U} for
`cpp_stl_11/msvc141-windows-x64`. I haven't tested these locally, but if
this change doesn't fix them, I don't know what would.

Instead of creating a set of integers that the enum represents, this
commit creates a set of the defined enum values themselves. This should
be more portable, as the code no longer needs to take into account what
integer type is the underlying type of the enum (despite my best
efforts, the existing approach didn't work in MSVC anyway for large
integer enum values, so the new approach seems to be the only way to
make it work on MSVC). Also, given that it was no longer necessary to
use `std::underlying_type` (which is only available since C++11), this
adds support for C++98. It required a workaround for the missing
initializer lists, but the result doesn't look too bad.
  • Loading branch information
generalmimon committed Aug 25, 2024
1 parent 5929d44 commit 75026ee
Showing 1 changed file with 38 additions and 19 deletions.
57 changes: 38 additions & 19 deletions shared/src/main/scala/io/kaitai/struct/languages/CppCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -893,21 +893,46 @@ class CppCompiler(

outHdr.dec
outHdr.puts("};")

outHdr.puts(s"static bool _is_defined_$enumClass($enumClass v);")
importListHdr.addSystem("set")
val inClassRef = types2class(curClass)
val enumClassAbs = s"$inClassRef::$enumClass"
val valuesSetAbsRef = s"$inClassRef::_values_$enumClass"
ensureMode(PrivateAccess)
if (config.cppConfig.useListInitializers) {
// NOTE: declaration and initialization must be separate in this case,
// NOTE: declaration and definition must be separate in this case,
// see https://stackoverflow.com/a/12856069
importListHdr.addSystem("set")
importListHdr.addSystem("type_traits") // because of `std::underlying_type`
// NOTE: `std::underlying_type` is only available since C++11. At the time
// of writing, it's not a problem because we're relying on list
// initializers anyway (which is a C++11 feature), but it will become a
// problem when we add C++98 support here.
outHdr.puts(s"static const std::set<std::underlying_type<$enumClass>::type> _values_$enumClass;")
val enumClassAbs = types2class(curClass ++ List(enumName))
val valuesSetAbsRef = s"${types2class(curClass)}::_values_$enumClass"
val setEntriesStr = enumColl.map { case (id, _) => translator.doIntLiteral(id) }.mkString(", ")
outSrc.puts(s"const std::set<std::underlying_type<$enumClassAbs>::type> $valuesSetAbsRef{$setEntriesStr};")
outHdr.puts(s"static const std::set<$enumClass> _values_$enumClass;")

outSrc.puts(s"const std::set<$enumClassAbs> $valuesSetAbsRef{")
outSrc.inc
enumColl.foreach { case (_, label) =>
outSrc.puts(s"$inClassRef::${value2Const(enumName, label.name)},")
}
outSrc.dec
outSrc.puts("};")
} else {
outHdr.puts(s"static std::set<$enumClass> _values_$enumClass;")
outHdr.puts(s"static std::set<$enumClass> _build_values_$enumClass();")

outSrc.puts(s"std::set<$enumClassAbs> $inClassRef::_build_values_$enumClass() {")
outSrc.inc
outSrc.puts(s"std::set<$enumClassAbs> _t;")
enumColl.foreach { case (_, label) =>
outSrc.puts(s"_t.insert($inClassRef::${value2Const(enumName, label.name)});")
}
outSrc.puts("return _t;")
outSrc.dec
outSrc.puts("}")
outSrc.puts(s"std::set<$enumClassAbs> $valuesSetAbsRef = $inClassRef::_build_values_$enumClass();")
}
ensureMode(PublicAccess)
outSrc.puts(s"bool $inClassRef::_is_defined_$enumClass($enumClassAbs v) {")
outSrc.inc
outSrc.puts(s"return $valuesSetAbsRef.find(v) != $valuesSetAbsRef.end();")
outSrc.dec
outSrc.puts("}")
}

override def classToString(toStringExpr: Ast.expr): Unit = {
Expand Down Expand Up @@ -1044,16 +1069,10 @@ class CppCompiler(
err: ValidationNotInEnumError,
errArgs: List[Ast.expr]
): Unit = {
if (!config.cppConfig.useListInitializers) {
throw new NotImplementedError(
"`valid/in-enum` is not yet implemented for C++98 (switch to C++11 or avoid this feature)"
)
}
val enumSpec = et.enumSpec.get
val inClassRef = types2class(enumSpec.name.dropRight(1))
val enumNameStr = type2class(enumSpec.name.last)
val valuesSetRef = s"$inClassRef::_values_$enumNameStr"
attrValidate(s"$valuesSetRef.find(${translator.translate(valueExpr)}) == $valuesSetRef.end()", err, errArgs)
attrValidate(s"!$inClassRef::_is_defined_$enumNameStr(${translator.translate(valueExpr)})", err, errArgs)
}

private def attrValidate(failCondExpr: String, err: KSError, errArgs: List[Ast.expr]): Unit = {
Expand Down

0 comments on commit 75026ee

Please sign in to comment.