| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This file implements outputLanguage for C++ |
| package source_generator |
| |
| import ( |
| "sort" |
| "strings" |
| ) |
| |
| type CPP struct{} |
| |
| // Gets header guards of the form: |
| // |
| // COBALT_REGISTRY_{PROJECT_NAME}_{CUSTOMER_NAME}_{NAMESPACE}_GEN_ |
| func getHeaderGuard(projectName, customerName string, namespaces []string) string { |
| base := toUpperSnakeCase("cobalt", "registry", projectName, customerName) |
| if len(namespaces) > 0 { |
| base = toUpperSnakeCase(base, strings.Join(namespaces, " ")) |
| } |
| return toUpperSnakeCase(base, "gen") + "_" |
| } |
| |
| func (CPP) getCommentPrefix() string { return "//" } |
| func (CPP) supportsTypeAlias() bool { return true } |
| |
| func (CPP) writeExtraHeader(so *sourceOutputter, projectName, customerName string, namespaces []string) { |
| if projectName == "" || customerName == "" { |
| so.writeLine("#pragma once") |
| } else { |
| guard := getHeaderGuard(projectName, customerName, namespaces) |
| so.writeLine("#ifndef " + guard) |
| so.writeLine("#define " + guard) |
| } |
| so.writeLine("") |
| so.writeLine("#include <stdint.h>") |
| so.writeLine("") |
| if so.options.shouldGenerateNameMaps() { |
| so.writeLine("#include <map>") |
| so.writeLine("#include <string>") |
| } |
| so.writeLine("#include <vector>") |
| so.writeLine("") |
| } |
| |
| func (CPP) writeExtraFooter(so *sourceOutputter, projectName, customerName string, namespaces []string) { |
| if projectName != "" && customerName != "" { |
| so.writeLine("") |
| so.writeLine("#endif // " + getHeaderGuard(projectName, customerName, namespaces)) |
| } |
| } |
| |
| func enumNamespace(name ...string) string { |
| name = append(name, "internal scope do not use") |
| return "__" + toSnakeCase(name...) + "__" |
| } |
| |
| func (CPP) writeEnumBegin(so *sourceOutputter, name ...string) { |
| so.writeLineFmt("namespace %s {", enumNamespace(name...)) |
| so.writeLine("enum Enum {") |
| } |
| |
| func (CPP) writeEnumEntry(so *sourceOutputter, value uint32, name ...string) { |
| so.writeLineFmt(" %s = %d,", toPascalCase(name...), value) |
| } |
| |
| // C++ allows duplicate keys in an enum, so nothing needs to be done here. |
| func (CPP) writeEnumAliasesBegin(so *sourceOutputter, name ...string) { |
| } |
| |
| func (CPP) writeEnumAlias(so *sourceOutputter, name, from, to []string) { |
| so.writeLineFmt(" %s = %s,", toPascalCase(to...), toPascalCase(from...)) |
| } |
| |
| func (CPP) writeEnumEnd(so *sourceOutputter, name ...string) { |
| so.writeLine("};") |
| so.writeLineFmt("} // %s", enumNamespace(name...)) |
| |
| so.writeLineFmt("using %s = %s::Enum;", toPascalCase(name...), enumNamespace(name...)) |
| } |
| |
| func (CPP) writeEnumExport(so *sourceOutputter, enumName, name []string) { |
| enum := toPascalCase(enumName...) |
| variant := toPascalCase(name...) |
| so.writeLineFmt("inline constexpr %s %s_%s = %s::%s;", enum, enum, variant, enum, variant) |
| } |
| |
| func (CPP) writeTypeAlias(so *sourceOutputter, from, to []string) { |
| so.writeLineFmt("using %s = %s;", toPascalCase(to...), toPascalCase(from...)) |
| } |
| |
| func getNamespaces(namespaces []string) string { |
| ns := make([]string, len(namespaces)) |
| for i, v := range namespaces { |
| ns[i] = toSnakeCase(v) |
| } |
| return strings.Join(ns, "::") |
| } |
| |
| func (CPP) writeNamespacesBegin(so *sourceOutputter, namespaces []string, outputFilename string) { |
| if len(namespaces) > 0 { |
| so.writeLineFmt("namespace %s {", getNamespaces(namespaces)) |
| } |
| } |
| |
| func (CPP) writeNamespacesEnd(so *sourceOutputter, namespaces []string, outputFilename string) { |
| if len(namespaces) > 0 { |
| so.writeLineFmt("} // %s", getNamespaces(namespaces)) |
| } |
| } |
| |
| func (CPP) writeConstUint32(so *sourceOutputter, value uint32, name ...string) { |
| name = append([]string{"k"}, name...) |
| so.writeLineFmt("inline constexpr uint32_t %s = %d;", toCamelCase(name...), value) |
| } |
| |
| func (CPP) writeConstInt64(so *sourceOutputter, value int64, name ...string) { |
| name = append([]string{"k"}, name...) |
| so.writeLineFmt("inline constexpr int64_t %s = %d;", toCamelCase(name...), value) |
| } |
| |
| func (CPP) writeConstMap(so *sourceOutputter, value map[uint32]string, name ...string) { |
| name = append([]string{"k"}, name...) |
| var keys []uint32 |
| for k := range value { |
| keys = append(keys, k) |
| } |
| sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) |
| |
| so.writeLineFmt("inline const std::map<std::string, uint32_t> %s = {", toCamelCase(name...)) |
| for _, id := range keys { |
| dimensionValue := value[id] |
| so.writeLineFmt(" {\"%s\", %d},", dimensionValue, id) |
| } |
| so.writeLineFmt("};") |
| } |
| |
| func (CPP) writeStringConstant(so *sourceOutputter, value string, name ...string) { |
| name = append([]string{"k"}, name...) |
| so.writeLineFmt("inline constexpr char %s[] = \"%s\";", toCamelCase(name...), value) |
| } |
| |
| func (CPP) writeStructBegin(so *sourceOutputter, name ...string) { |
| so.writeLineFmt("struct %s {", toPascalCase(name...)) |
| } |
| |
| func (CPP) writeStructField(so *sourceOutputter, name, typeName []string) { |
| so.writeLineFmt(" %s %s;", toPascalCase(typeName...), toSnakeCase(name...)) |
| } |
| |
| func (CPP) writeToVectorMethod(so *sourceOutputter, structName []string, fields [][]string) { |
| so.writeLine("") |
| so.writeLineFmt(" [[nodiscard]] std::vector<uint32_t> ToVector() const {") |
| so.writeLine(" return {") |
| for _, field := range fields { |
| so.writeLineFmt(" %s,", toSnakeCase(field...)) |
| } |
| so.writeLine(" };") |
| so.writeLine(" }") |
| } |
| |
| func (CPP) writeStructEnd(so *sourceOutputter) { |
| so.writeLine("};") |
| } |
| |
| // Returns an output formatter that will output the contents of a C++ header |
| // file that contains a variable declaration for a string literal that contains |
| // the base64-encoding of the serialized proto. |
| // |
| // varName will be the name of the variable containing the base64-encoded serialized proto. |
| // namespace is a list of nested namespaces inside of which the variable will be defined. |
| // If forTesting is true, a constant will be generated for each report ID, based on the report's name. |
| func CppOutputFactory(varName string, namespace []string, options generatorOptions) OutputFormatter { |
| return newSourceOutputterWithNamespaces(CPP{}, varName, namespace, options).getOutputFormatter() |
| } |