LLZK 2.1.1
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
CommonCAPIGen.h
Go to the documentation of this file.
1//===- CommonCAPIGen.h - Common utilities for C API generation ------------===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9//
10// Common utilities shared between all CAPI generators (ops, attrs, types)
11//
12//===----------------------------------------------------------------------===//
13
14#pragma once
15
16#include <mlir/TableGen/Dialect.h>
17
18#include <llvm/ADT/StringExtras.h>
19#include <llvm/ADT/StringRef.h>
20#include <llvm/ADT/StringSwitch.h>
21#include <llvm/Support/CommandLine.h>
22#include <llvm/Support/FormatVariadic.h>
23
24#include <memory>
25#include <string>
26
27constexpr bool WARN_SKIPPED_METHODS = false;
28
30template <typename S> inline void warnSkipped(const S &methodName, const std::string &message) {
32 llvm::errs() << "Warning: Skipping method '" << methodName << "' - " << message << '\n';
33 }
34}
35
37template <typename S>
38inline void warnSkippedNoConversion(const S &methodName, const std::string &cppType) {
40 warnSkipped(methodName, "no conversion to C API type for '" + cppType + '\'');
41 }
42}
43
44// Forward declarations for Clang classes
45namespace clang {
46class Lexer;
47class SourceManager;
48} // namespace clang
49
50// Shared command-line options used by all CAPI generators
51extern llvm::cl::OptionCategory OpGenCat;
52extern llvm::cl::opt<std::string> DialectName;
53extern llvm::cl::opt<std::string> FunctionPrefix;
54
55// Shared flags for controlling code generation
56extern llvm::cl::opt<bool> GenIsA;
57extern llvm::cl::opt<bool> GenOpBuild;
58extern llvm::cl::opt<bool> GenOpOperandGetters;
59extern llvm::cl::opt<bool> GenOpOperandSetters;
60extern llvm::cl::opt<bool> GenOpAttributeGetters;
61extern llvm::cl::opt<bool> GenOpAttributeSetters;
62extern llvm::cl::opt<bool> GenOpRegionGetters;
63extern llvm::cl::opt<bool> GenOpResultGetters;
64extern llvm::cl::opt<bool> GenTypeOrAttrGet;
65extern llvm::cl::opt<bool> GenTypeOrAttrParamGetters;
66extern llvm::cl::opt<bool> GenExtraClassMethods;
67
75inline std::string toPascalCase(mlir::StringRef str) {
76 if (str.empty()) {
77 return "";
78 }
79
80 std::string result;
81 result.reserve(str.size());
82 llvm::raw_string_ostream resultStream(result);
83 bool capitalizeNext = true;
84
85 for (char c : str) {
86 if (c == '_' || c == ':') {
87 capitalizeNext = true;
88 } else {
89 resultStream << (capitalizeNext ? llvm::toUpper(c) : c);
90 capitalizeNext = false;
91 }
92 }
93
94 return result;
95}
96
100inline bool isIntegerType(mlir::StringRef type) {
101 // Consume optional root namespace token
102 type.consume_front("::");
103 // Handle special names first
104 if (type == "signed" || type == "unsigned" || type == "size_t" || type == "char32_t" ||
105 type == "char16_t" || type == "char8_t" || type == "wchar_t") {
106 return true;
107 }
108 // Handle standard integer types with optional signed/unsigned prefix
109 type.consume_front("signed ") || type.consume_front("unsigned ");
110 if (type == "char" || type == "int" || type == "short" || type == "short int" || type == "long" ||
111 type == "long int" || type == "long long" || type == "long long int") {
112 return true;
113 }
114 // Handle fixed-width integer types (https://cppreference.com/w/cpp/types/integer.html)
115 type.consume_front("std::"); // optional
116 if (type.consume_back("_t") && (type.consume_front("int") || type.consume_front("uint"))) {
117 // intmax_t, intptr_t, uintmax_t, uintptr_t
118 if (type == "max" || type == "ptr") {
119 return true;
120 }
121 // Optional "_fast" or "_least" followed by bit width to cover the rest
122 type.consume_front("_fast") || type.consume_front("_least");
123 if (type == "8" || type == "16" || type == "32" || type == "64") {
124 return true;
125 }
126 }
127 return false;
128}
129
136inline bool isPrimitiveType(mlir::StringRef cppType) {
137 cppType.consume_front("::");
138 return cppType == "void" || cppType == "bool" || cppType == "float" || cppType == "double" ||
139 cppType == "long double" || isIntegerType(cppType);
140}
141
145inline bool isCppModifierKeyword(mlir::StringRef tokenText) {
146 return llvm::StringSwitch<bool>(tokenText)
147 .Case("inline", true)
148 .Case("static", true)
149 .Case("virtual", true)
150 .Case("explicit", true)
151 .Case("constexpr", true)
152 .Case("consteval", true)
153 .Case("extern", true)
154 .Case("mutable", true)
155 .Case("friend", true)
156 .Default(false);
157}
158
162inline bool isCppLanguageConstruct(mlir::StringRef methodName) {
163 return llvm::StringSwitch<bool>(methodName)
164 .Case("if", true)
165 .Case("for", true)
166 .Case("while", true)
167 .Case("switch", true)
168 .Case("return", true)
169 .Case("sizeof", true)
170 .Case("decltype", true)
171 .Case("alignof", true)
172 .Case("typeid", true)
173 .Case("static_assert", true)
174 .Case("noexcept", true)
175 .Default(false);
176}
177
181inline bool isAPIntType(mlir::StringRef cppType) {
182 cppType.consume_front("::");
183 cppType.consume_front("llvm::") || cppType.consume_front("mlir::");
184 return cppType == "APInt";
185}
186
190inline bool isArrayRefType(mlir::StringRef cppType) {
191 cppType.consume_front("::");
192 cppType.consume_front("llvm::") || cppType.consume_front("mlir::");
193 return cppType.starts_with("ArrayRef<");
194}
195
197inline mlir::StringRef extractArrayRefElementType(mlir::StringRef cppType) {
198 assert(isArrayRefType(cppType) && "must check `isArrayRefType()` outside");
199
200 // Remove "ArrayRef<" prefix and ">" suffix
201 cppType.consume_front("::");
202 cppType.consume_front("llvm::") || cppType.consume_front("mlir::");
203 cppType.consume_front("ArrayRef<") && cppType.consume_back(">");
204 return cppType;
205}
206
216public:
220 explicit ClangLexerContext(mlir::StringRef source, mlir::StringRef bufferName = "input");
221
224 clang::Lexer &getLexer() const;
225
228 clang::SourceManager &getSourceManager() const;
229
232 bool isValid() const { return lexer != nullptr; }
233
234private:
235 struct Impl;
236 std::unique_ptr<Impl> impl;
237 clang::Lexer *lexer = nullptr;
238};
239
244 std::string type;
246 std::string name;
247
251 MethodParameter(const std::string &paramType, const std::string &paramName)
252 : type(mlir::StringRef(paramType).trim().str()),
253 name(mlir::StringRef(paramName).trim().str()) {}
254};
255
262 std::string returnType;
264 std::string methodName;
266 std::string documentation;
268 bool isConst = false;
270 bool hasParameters = false;
272 std::vector<MethodParameter> parameters;
273};
274
301llvm::SmallVector<ExtraMethod> parseExtraMethods(mlir::StringRef extraDecl);
302
307bool matchesMLIRClass(mlir::StringRef cppType, mlir::StringRef typeName);
308
312std::optional<std::string> tryCppTypeToCapiType(mlir::StringRef cppType);
313
320std::string mapCppTypeToCapiType(mlir::StringRef cppType);
321
327std::optional<std::string> mapCapiTypeToBasicCppType(mlir::StringRef capiType);
328
330struct Generator {
331 Generator(std::string_view recordKind, llvm::raw_ostream &outputStream)
332 : kind(recordKind), os(outputStream), dialectNameCapitalized(toPascalCase(DialectName)) {}
333 virtual ~Generator() = default;
334
338 virtual void
339 setNamespaceAndClassName(const mlir::tblgen::Dialect &d, mlir::StringRef cppClassName) {
340 this->dialectNamespace = d.getCppNamespace();
341 this->className = cppClassName;
342 }
343
346 virtual void genExtraMethods(mlir::StringRef extraDecl) const {
347 if (extraDecl.empty()) {
348 return;
349 }
350 for (const ExtraMethod &method : parseExtraMethods(extraDecl)) {
351 genExtraMethod(method);
352 }
353 }
354
357 virtual void genExtraMethod(const ExtraMethod &method) const = 0;
358
359protected:
360 std::string kind;
361 llvm::raw_ostream &os;
363 mlir::StringRef dialectNamespace;
364 mlir::StringRef className;
365};
366
368struct HeaderGenerator : public Generator {
370 ~HeaderGenerator() override = default;
371
372 virtual void genPrologue() const {
373 os << R"(
374#include "llzk-c/Builder.h"
375#include <mlir-c/IR.h>
377#ifdef __cplusplus
378extern "C" {
379#endif
380)";
381 }
382
383 virtual void genEpilogue() const {
384 os << R"(
385#ifdef __cplusplus
386}
387#endif
388)";
389 }
390
391 virtual void genIsADecl() const {
392 static constexpr char fmt[] = R"(
394MLIR_CAPI_EXPORTED bool {0}{1}IsA_{2}_{3}(Mlir{1});
395)";
396 assert(!dialectNamespace.empty() && "Dialect must be set");
397 os << llvm::formatv(
398 fmt,
399 FunctionPrefix, // {0}
400 kind, // {1}
402 className, // {3}
403 dialectNamespace // {4}
404 );
405 }
406
408 void genExtraMethod(const ExtraMethod &method) const override {
409 // Convert return type to C API type, skip if it can't be converted
410 std::optional<std::string> capiReturnTypeOpt = tryCppTypeToCapiType(method.returnType);
411 if (!capiReturnTypeOpt.has_value()) {
413 return;
414 }
415 std::string capiReturnType = capiReturnTypeOpt.value();
416
417 // Build parameter list
418 std::string paramList;
419 llvm::raw_string_ostream paramListStream(paramList);
420 paramListStream << llvm::formatv("Mlir{0} inp", kind);
421 for (const auto &param : method.parameters) {
422 // Convert C++ type to C API type for parameter, skip if it can't be converted
423 std::optional<std::string> capiParamTypeOpt = tryCppTypeToCapiType(param.type);
424 if (!capiParamTypeOpt.has_value()) {
425 warnSkippedNoConversion(method.methodName, param.type);
426 return;
427 }
428 const std::string &capiParamType = capiParamTypeOpt.value();
429 paramListStream << ", " << capiParamType << ' ' << param.name;
430 }
431
432 // Generate declaration
433 if (method.documentation.empty()) {
434 os << llvm::formatv("\n/// {0}\n", method.methodName);
435 } else {
436 os << llvm::formatv("\n{0}\n", method.documentation);
437 }
438 os << llvm::formatv(
439 "MLIR_CAPI_EXPORTED {0} {1}{2}_{3}{4}({5});\n",
440 capiReturnType, // {0}
441 FunctionPrefix, // {1}
443 className, // {3}
444 toPascalCase(method.methodName), // {4}
445 paramList // {5}
446 );
447 }
448};
449
453 ~ImplementationGenerator() override = default;
454
455 virtual void genPrologue() const {}
456
457 virtual void genIsAImpl() const {
458 static constexpr char fmt[] = R"(
459bool {0}{1}IsA_{2}_{3}(Mlir{1} inp) {{
460 return llvm::isa<{3}>(unwrap(inp));
461}
462)";
463 assert(!className.empty() && "className must be set");
465 }
466
468 void genExtraMethod(const ExtraMethod &method) const override {
469 // Convert return type to C API type, skip if it can't be converted
470 std::optional<std::string> capiReturnTypeOpt = tryCppTypeToCapiType(method.returnType);
471 if (!capiReturnTypeOpt.has_value()) {
473 return;
474 }
475 std::string capiReturnType = capiReturnTypeOpt.value();
476
477 // Build the return statement prefix and suffix
478 std::string returnPrefix;
479 std::string returnSuffix;
480 mlir::StringRef cppReturnType = method.returnType;
481
482 if (cppReturnType == "void") {
483 // "void" type doesn't even need "return"
484 returnPrefix = "";
485 returnSuffix = "";
486 } else {
487 // Check if return needs wrapping
488 if (isPrimitiveType(cppReturnType)) {
489 // Primitive types don't need wrapping
490 returnPrefix = "return ";
491 returnSuffix = "";
492 } else if (capiReturnType.starts_with("Mlir") || isAPIntType(cppReturnType)) {
493 // MLIR C API types and APInt type need wrapping
494 returnPrefix = "return wrap(";
495 returnSuffix = ")";
496 } else {
497 return;
498 }
499 }
500
501 // Build parameter list for C API function signature
502 std::string paramList;
503 llvm::raw_string_ostream paramListStream(paramList);
504 paramListStream << llvm::formatv("Mlir{0} inp", kind);
505 for (const auto &param : method.parameters) {
506 // Convert C++ type to C API type for parameter, skip if it can't be converted
507 std::optional<std::string> capiParamTypeOpt = tryCppTypeToCapiType(param.type);
508 if (!capiParamTypeOpt.has_value()) {
509 warnSkippedNoConversion(method.methodName, param.type);
510 return;
511 }
512 const std::string &capiParamType = capiParamTypeOpt.value();
513 paramListStream << ", " << capiParamType << ' ' << param.name;
514 }
515
516 // Build argument list for C++ method call
517 std::string argList;
518 llvm::raw_string_ostream argListStream(argList);
519 for (size_t i = 0; i < method.parameters.size(); ++i) {
520 if (i > 0) {
521 argListStream << ", ";
522 }
523 const auto &param = method.parameters[i];
524
525 // Check if parameter needs unwrapping
526 mlir::StringRef cppParamType = param.type;
527 if (isPrimitiveType(cppParamType)) {
528 // Primitive types don't need unwrapping
529 argListStream << param.name;
530 } else if (isAPIntType(cppParamType)) {
531 // APInt needs unwrapping
532 argListStream << "unwrap(" << param.name << ')';
533 } else {
534 // Convert C++ type to C API type for parameter, skip if it can't be converted
535 std::optional<std::string> capiParamTypeOpt = tryCppTypeToCapiType(cppParamType);
536 if (capiParamTypeOpt.has_value() && capiParamTypeOpt->starts_with("Mlir")) {
537 // MLIR C API types need unwrapping
538 argListStream << "unwrap(" << param.name << ')';
539 } else {
540 warnSkippedNoConversion(method.methodName, cppParamType.str());
541 return;
542 }
543 }
544 }
545
546 // Generate implementation
547 os << '\n';
548 os << llvm::formatv(
549 "{0} {1}{2}_{3}{4}({5}) {{\n",
550 capiReturnType, // {0}
551 FunctionPrefix, // {1}
553 className, // {3}
554 toPascalCase(method.methodName), // {4}
555 paramList // {5}
556 );
557 os << llvm::formatv(
558 " {0}llvm::cast<{1}>(unwrap(inp)).{2}({3}){4};\n",
559 returnPrefix, // {0}
560 className, // {1}
561 method.methodName, // {2}
562 argList, // {3}
563 returnSuffix // {4}
564 );
565 os << "}\n";
566 }
567};
568
570struct TestGenerator : public Generator {
572 ~TestGenerator() override = default;
573
575 virtual void genTestClassPrologue() const {
576 static constexpr char fmt[] = "class {0}{1}LinkTests : public CAPITest {{};\n";
577 os << llvm::formatv(fmt, dialectNameCapitalized, kind);
578 }
579
581 virtual void genIsATest() const {
582 static constexpr char fmt[] = R"(
584TEST_F({2}{1}LinkTests, IsA_{2}_{3}) {{
585 auto test{1} = createIndex{1}();
586
587 // This will always return false since `createIndex*` returns an MLIR builtin
588 EXPECT_FALSE({0}{1}IsA_{2}_{3}(test{1}));
589
590 {4}(test{1});
591}
592)";
593 assert(!className.empty() && "className must be set");
594 os << llvm::formatv(
595 fmt,
596 FunctionPrefix, // {0}
597 kind, // {1}
599 className, // {3}
600 genCleanup() // {4}
601 );
602 }
603
605 void genExtraMethod(const ExtraMethod &method) const override {
606 // Convert return type to C API type, skip if it can't be converted
607 std::optional<std::string> capiReturnTypeOpt = tryCppTypeToCapiType(method.returnType);
608 if (!capiReturnTypeOpt.has_value()) {
610 return;
611 }
612
613 // Build parameter list for dummy values
614 std::string dummyParams;
615 llvm::raw_string_ostream dummyParamsStream(dummyParams);
616 std::string paramList;
617 llvm::raw_string_ostream paramListStream(paramList);
618
619 for (const auto &param : method.parameters) {
620 // Convert C++ type to C API type for parameter, skip if it can't be converted
621 std::optional<std::string> capiParamTypeOpt = tryCppTypeToCapiType(param.type);
622 if (!capiParamTypeOpt.has_value()) {
623 warnSkippedNoConversion(method.methodName, param.type);
624 return;
625 }
626 const std::string &capiParamType = capiParamTypeOpt.value();
627 std::string name = param.name;
628
629 // Generate dummy value creation for each parameter
630 if (capiParamType == "bool") {
631 dummyParamsStream << " bool " << name << " = false;\n";
632 } else if (capiParamType == "MlirValue") {
633 dummyParamsStream << " auto " << name << " = mlirOperationGetResult(testOp, 0);\n";
634 } else if (capiParamType == "MlirType") {
635 dummyParamsStream << " auto " << name << " = createIndexType();\n";
636 } else if (capiParamType == "MlirAttribute") {
637 dummyParamsStream << " auto " << name << " = createIndexAttribute();\n";
638 } else if (capiParamType == "MlirStringRef") {
639 dummyParamsStream << " auto " << name << " = mlirStringRefCreateFromCString(\"\");\n";
640 } else if (isIntegerType(capiParamType)) {
641 dummyParamsStream << " " << capiParamType << ' ' << name << " = 0;\n";
642 } else {
643 // For unknown types, create a default-initialized variable
644 dummyParamsStream << " " << capiParamType << ' ' << name << " = {};\n";
645 }
646
647 paramListStream << ", " << name;
648 }
649
650 static constexpr char fmt[] = R"(
652TEST_F({2}{1}LinkTests, {0}_{3}_{4}) {{
653 auto test{1} = createIndex{1}();
654
655 if ({0}{1}IsA_{2}_{3}(test{1})) {{
656{5}
657 (void){0}{2}_{3}{4}(test{1}{6});
658 }
659
660 {7}(test{1});
661}
662)";
663 assert(!className.empty() && "className must be set");
664 os << llvm::formatv(
665 fmt,
666 FunctionPrefix, // {0}
667 kind, // {1}
669 className, // {3}
670 toPascalCase(method.methodName), // {4}
671 dummyParams, // {5}
672 paramList, // {6}
673 genCleanup() // {7}
674 );
675 }
676
686 virtual std::string genCleanup() const {
687 // The default case is to just comment out the rest of the cleanup line
688 return "//";
689 }
690};
mlir::StringRef extractArrayRefElementType(mlir::StringRef cppType)
Extract element type from ArrayRef<...>
llvm::cl::OptionCategory OpGenCat
llvm::cl::opt< bool > GenOpOperandSetters
llvm::cl::opt< bool > GenTypeOrAttrParamGetters
bool isPrimitiveType(mlir::StringRef cppType)
Check if a C++ type is a known primitive type.
llvm::cl::opt< bool > GenTypeOrAttrGet
void warnSkippedNoConversion(const S &methodName, const std::string &cppType)
Print warning about skipping a function due to no conversion of C++ type to C API type.
llvm::cl::opt< bool > GenIsA
std::string mapCppTypeToCapiType(mlir::StringRef cppType)
Map C++ type to corresponding C API type.
llvm::cl::opt< bool > GenOpBuild
llvm::cl::opt< std::string > DialectName
bool isCppModifierKeyword(mlir::StringRef tokenText)
Check if a token text represents a C++ modifier/specifier keyword.
bool isAPIntType(mlir::StringRef cppType)
Check if a C++ type is APInt.
llvm::cl::opt< std::string > FunctionPrefix
std::optional< std::string > tryCppTypeToCapiType(mlir::StringRef cppType)
Convert C++ type to MLIR C API type.
bool isArrayRefType(mlir::StringRef cppType)
Check if a C++ type is an ArrayRef type.
llvm::cl::opt< bool > GenOpRegionGetters
constexpr bool WARN_SKIPPED_METHODS
bool isIntegerType(mlir::StringRef type)
Check if a C++ type is a known integer type.
llvm::cl::opt< bool > GenOpResultGetters
llvm::cl::opt< bool > GenOpAttributeGetters
bool matchesMLIRClass(mlir::StringRef cppType, mlir::StringRef typeName)
Check if a C++ type matches an MLIR type pattern.
llvm::cl::opt< bool > GenOpAttributeSetters
std::optional< std::string > mapCapiTypeToBasicCppType(mlir::StringRef capiType)
Map C API type to corresponding basic (not dialect-defined) C++ type.
llvm::cl::opt< bool > GenOpOperandGetters
llvm::SmallVector< ExtraMethod > parseExtraMethods(mlir::StringRef extraDecl)
Parse method declarations from an extraClassDeclaration using Clang's Lexer.
std::string toPascalCase(mlir::StringRef str)
Convert names separated by underscore or colon to PascalCase.
llvm::cl::opt< bool > GenExtraClassMethods
void warnSkipped(const S &methodName, const std::string &message)
Print warning about skipping a function.
bool isCppLanguageConstruct(mlir::StringRef methodName)
Check if a method name represents a C++ control flow keyword or language construct.
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation source
Definition LICENSE.txt:28
clang::SourceManager & getSourceManager() const
Get the source manager instance.
bool isValid() const
Check if the lexer was successfully created.
ClangLexerContext(mlir::StringRef source, mlir::StringRef bufferName="input")
Construct a lexer context for the given source code.
clang::Lexer & getLexer() const
Get the lexer instance.
Structure to represent a parsed method signature from an extraClassDeclaration
bool isConst
Whether the method is const-qualified.
bool hasParameters
Whether the method has parameters (unsupported for now)
std::vector< MethodParameter > parameters
The parameters of the method.
std::string returnType
The C++ return type of the method.
std::string methodName
The name of the method.
std::string documentation
Properly escaped documentation comment (if any)
virtual ~Generator()=default
Generator(std::string_view recordKind, llvm::raw_ostream &outputStream)
mlir::StringRef className
mlir::StringRef dialectNamespace
virtual void genExtraMethods(mlir::StringRef extraDecl) const
Generate code for extra methods from an extraClassDeclaration
virtual void setNamespaceAndClassName(const mlir::tblgen::Dialect &d, mlir::StringRef cppClassName)
Set the dialect and class name for code generation.
virtual void genExtraMethod(const ExtraMethod &method) const =0
Generate code for an extra method.
std::string dialectNameCapitalized
llvm::raw_ostream & os
std::string kind
Generator for common C header file elements.
Generator(std::string_view recordKind, llvm::raw_ostream &outputStream)
virtual void genPrologue() const
void genExtraMethod(const ExtraMethod &method) const override
Generate declaration for an extra method from an extraClassDeclaration
~HeaderGenerator() override=default
virtual void genEpilogue() const
virtual void genIsADecl() const
Generator for common C implementation file elements.
Generator(std::string_view recordKind, llvm::raw_ostream &outputStream)
virtual void genIsAImpl() const
void genExtraMethod(const ExtraMethod &method) const override
Generate implementation for an extra method from an extraClassDeclaration
virtual void genPrologue() const
~ImplementationGenerator() override=default
std::string name
The name of the parameter.
std::string type
The C++ type of the parameter.
MethodParameter(const std::string &paramType, const std::string &paramName)
Construct a new Method Parameter object.
Generator for common test implementation file elements.
virtual void genTestClassPrologue() const
Generate the test class prologue.
Generator(std::string_view recordKind, llvm::raw_ostream &outputStream)
~TestGenerator() override=default
void genExtraMethod(const ExtraMethod &method) const override
Generate test for an extra method from extraClassDeclaration.
virtual void genIsATest() const
Generate IsA test for a class.
virtual std::string genCleanup() const
Generate cleanup code for test methods.