Add CodeWriter utility class.

Helps simplify code generation code.  Instead of this:
  code += "inline const " + cpp_qualified_name + " *Get";
  code += name;
  code += "(const void *buf) {\n  return flatbuffers::GetRoot<";
  code += cpp_qualified_name + ">(buf);\n}\n\n";

You do this:
  code.SetValue("NAME", struct_def.name);
  code.SetValue("CPP_NAME", cpp_qualified_name);
  code += "inline const {{CPP_NAME}} *Get{{NAME}}(const void *buf) {";
  code += "  return flatbuffers::GetRoot<{{CPP_NAME}}>(buf);";
  code += "}";
  code += "";

Updated the CPP code generator to use the CodeWriter class.  Most of the
changes in the generated code are white-space changes, esp. around new
lines (since the code generator class automatically appends new lines
when appending a string).  Actual code changes include:

* Renamed "rehasher" to "_rehasher" for consistency with other args in
  Pack function.

* Renamed "union_obj" to "obj: in UnPack function.

* Always do "(void)_o;" to prevent unused variable warning in Create
  function (instead of only doing it if there are no fields) in order
  to avoid two-passes.

* Renamed padding variables from __paddingX to paddingX__.
  "Each name that contains a double underscore (_ _) [...] is reserved
   to the implementation for any use."  C++ standards 17.4.3.1.2.

* Add braces around switch cases.

* Calculate index as a separate statement in EnumName function, eg.
    const size_t index = ...;
    return EnumNamesX()[index];
  vs.
    return EnumNamesX()[...];

* Stored end table offset in variable in Finish() functions, eg.
    const auto end = fbb_.EndTable(start_, ...);
    auto o = flatbuffers::Offset<T>(end);
  vs.
    auto o = flatbuffers::Offset<T>(fbb_.EndTable(start, ...));

* Separate reinterpret_cast calls from function calls in Union
  functions, eg.
    auto ptr = reinterpret_cast<const T *>(obj);
    return ptr->UnPack(resolver);
  vs.
    return reinterpret_cast<const T *>(obj)->UnPack(resolver);

* Removed unecessary (void)(padding__X) no-ops from constructors, eg.
    Test(int16_t a, int8_t b) : ... {
      (void)__padding0;  // <-- Removed this line.
    }

In the idl_gen_cpp.cpp file itself, I refactored some code generation into
new functions: GenParam, GenNativeTable, GenVerifyCall, GenBuilders,
GenUnpackFieldStatement, and GenCreateParam.

Change-Id: I727b1bd8719d05b7ce33cbce00eb58fda817b25d
This commit is contained in:
Wouter van Oortmerssen
2017-01-13 17:44:42 -08:00
parent 1a21b54560
commit 7b94eab2b1
12 changed files with 2431 additions and 1306 deletions

View File

@@ -17,25 +17,63 @@
#ifndef FLATBUFFERS_CODE_GENERATORS_H_
#define FLATBUFFERS_CODE_GENERATORS_H_
#include <map>
#include <sstream>
#include "flatbuffers/idl.h"
namespace flatbuffers {
// Utility class to assist in generating code through use of text templates.
//
// Example code:
// CodeWriter code;
// code.SetValue("NAME", "Foo");
// code += "void {{NAME}}() { printf("%s", "{{NAME}}"); }";
// code.SetValue("NAME", "Bar");
// code += "void {{NAME}}() { printf("%s", "{{NAME}}"); }";
// std::cout << code.ToString() << std::endl;
//
// Output:
// void Foo() { printf("%s", "Foo"); }
// void Bar() { printf("%s", "Bar"); }
class CodeWriter {
public:
CodeWriter() {}
// Clears the current "written" code.
void Clear() {
stream_.str("");
stream_.clear();
}
// Associates a key with a value. All subsequent calls to operator+=, where
// the specified key is contained in {{ and }} delimiters will be replaced by
// the given value.
void SetValue(const std::string& key, const std::string& value) {
value_map_[key] = value;
}
// Appends the given text to the generated code as well as a newline
// character. Any text within {{ and }} delimeters is replaced by values
// previously stored in the CodeWriter by calling SetValue above. The newline
// will be suppressed if the text ends with the \\ character.
void operator+=(std::string text);
// Returns the current contents of the CodeWriter as a std::string.
std::string ToString() const { return stream_.str(); }
private:
std::map<std::string, std::string> value_map_;
std::stringstream stream_;
};
class BaseGenerator {
public:
virtual bool generate() = 0;
static std::string NamespaceDir(const Parser &parser,
const std::string &path,
const Namespace &ns) {
EnsureDirExists(path.c_str());
if (parser.opts.one_file) return path;
std::string namespace_dir = path; // Either empty or ends in separator.
auto &namespaces = ns.components;
for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
namespace_dir += *it + kPathSeparator;
EnsureDirExists(namespace_dir.c_str());
}
return namespace_dir;
}
const Namespace &ns);
protected:
BaseGenerator(const Parser &parser, const std::string &path,
@@ -53,43 +91,15 @@ class BaseGenerator {
BaseGenerator &operator=(const BaseGenerator &);
BaseGenerator(const BaseGenerator &);
std::string NamespaceDir(const Namespace &ns) const {
return BaseGenerator::NamespaceDir(parser_, path_, ns);
}
std::string NamespaceDir(const Namespace &ns) const;
const char *FlatBuffersGeneratedWarning() {
return "automatically generated by the FlatBuffers compiler,"
" do not modify\n\n";
}
static const char *FlatBuffersGeneratedWarning();
bool IsEverythingGenerated() const {
for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
++it) {
if (!(*it)->generated) return false;
}
for (auto it = parser_.structs_.vec.begin();
it != parser_.structs_.vec.end(); ++it) {
if (!(*it)->generated) return false;
}
return true;
}
bool IsEverythingGenerated() const;
static std::string FullNamespace(const char *separator, const Namespace &ns) {
std::string namespace_name;
auto &namespaces = ns.components;
for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
if (namespace_name.length()) namespace_name += separator;
namespace_name += *it;
}
return namespace_name;
}
static std::string FullNamespace(const char *separator, const Namespace &ns);
static std::string LastNamespacePart(const Namespace &ns) {
if (!ns.components.empty())
return ns.components.back();
else
return std::string("");
}
static std::string LastNamespacePart(const Namespace &ns);
// tracks the current namespace for early exit in WrapInNameSpace
// c++, java and csharp returns a different namespace from
@@ -100,17 +110,9 @@ class BaseGenerator {
// Ensure that a type is prefixed with its namespace whenever it is used
// outside of its namespace.
std::string WrapInNameSpace(const Namespace *ns,
const std::string &name) const {
if (CurrentNameSpace() == ns) return name;
std::string qualified_name = qualifying_start_;
for (auto it = ns->components.begin(); it != ns->components.end(); ++it)
qualified_name += *it + qualifying_separator_;
return qualified_name + name;
}
const std::string &name) const;
std::string WrapInNameSpace(const Definition &def) const {
return WrapInNameSpace(def.defined_namespace, def.name);
}
std::string WrapInNameSpace(const Definition &def) const;
const Parser &parser_;
const std::string &path_;
@@ -119,6 +121,17 @@ class BaseGenerator {
const std::string qualifying_separator_;
};
struct CommentConfig {
const char *first_line;
const char *content_line_prefix;
const char *last_line;
};
extern void GenComment(const std::vector<std::string> &dc,
std::string *code_ptr,
const CommentConfig *config,
const char *prefix = "");
} // namespace flatbuffers
#endif // FLATBUFFERS_CODE_GENERATORS_H_