blob: 5c6128840f33f28aaef5e8b6e9d81c73a4bd3df6 [file] [log] [blame]
// 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()
}