capi

The yace emitter target, named capi, produces a C API consisting of the following files:

  • lib{meta.prefix}.h – Library header bundle

  • lib{meta.prefix}_core.h – The core API declarations

  • lib{meta.prefix}_pp.h – Pretty-printer definitions

  • {meta.prefix}_pp.c – Pretty-printer implementation

And, not part of the API itself is:

  • {meta.prefix}_check.cmain() program checking generated code

The following section shows the details of how to generate a C API from a yace interface definition language Storage Format file.

Example

Below, yace is invoked on the models/example.yaml from the yace git-repository.

yace models/example.yaml --emit capi --output output
output
├── capi
│   ├── a.out
│   ├── clang-format-c.clang-format
│   ├── clang-format-h.clang-format
│   ├── clang-format.log
│   ├── doxygen.conf
│   ├── doxygen.log
│   ├── doxyreport
│   ├── foo_check.c
│   ├── foo_pp.c
│   ├── gcc.log
│   ├── libfoo.h
│   ├── libfoo_core.h
│   └── libfoo_pp.h
└── example.yaml

2 directories, 13 files

Describe the parts of the generated output. Show the content of the most interesting parts

C API

../../../../output/capi/libfoo.h
/**
 * foo
 *
 * Library bundle header for foo
 *
 * The bundle-header provides a single point of entry for library users to include. This is done
 * such that all the other headers do not have to:
 *
 * - Include other headers (See Rob Pike)
 * - Use include-guard
 * - Use C++ extern
 *
 * Additionally, this allows for composition when using the foo library. In case the
 * library user have another definition for e.g. integer types, or only wants a subset etc. then
 * they can compose utilization that serves them best.
 *
 * ------------------------------------------------------------------------------------------------
 * Copyright (C) Foo Bar <foo@example.com>
 * SPDX-License-Identifier: Unknown License
 *
 * @file libfoo.h
 * ------------------------------------------------------------------------------------------------
 * NOTE: This file is auto-generated using yace: https://github.com/safl/yace
 */
#ifndef __LIBFOO_H
#define __LIBFOO_H

#ifdef __cplusplus
extern "C" {
#endif

#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <libfoo_core.h>
#include <libfoo_pp.h>

#ifdef __cplusplus
}
#endif

#endif /* __LIBFOO_H */
../../../../output/capi/libfoo_core.h
/**
 * foo; Core API
 *
 * Brief Description
 *
 * Full Description
 *
 * ------------------------------------------------------------------------------------------------
 * Copyright (C) Foo Bar <foo@example.com>
 * SPDX-License-Identifier: Unknown License
 *
 * @file libfoo.h
 * ------------------------------------------------------------------------------------------------
 * NOTE: This file is created using yace: https://github.com/safl/yace
 */

#define PLOT_SERIAL        "WYRD1234"
#define PLOT_FOO           0xACDC
#define PLOT_VERSION_MAJOR 1
#define PLOT_VERSION_MINOR 2
#define PLOT_VERSION_PATCH 3

/**
 * Description of enum
 *
 * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque accumsan massa est, ut
 * ullamcorper lectus malesuada sit amet. Donec gravida nibh aliquam mattis rhoncus. Quisque
 * sollicitudin ultricies orci, condimentum blandit lorem feugiat at. Aliquam tempus metus et nulla
 * eleifend, eu dapibus risus pellentesque. Duis fermentum bibendum ligula in pharetra. Curabitur
 * eget urna tempus, tempor dui sed, tempus lorem. Phasellus eu aliquam neque.
 *
 * @enum plot_options
 */
enum plot_options {
	PLOT_OPTIONS_PNG = 0x1, ///< First thing
	PLOT_OPTIONS_PDF = 0x2, ///< Second thing
};

/**
 * Description of structure
 *
 * @struct coordinate
 */
struct coordinate {
	int32_t x; ///< X Coordinate
	int32_t y; ///< Y Coordinate
	int32_t z; ///< Z Coordinate
};

/**
 * Description of a feature...
 *
 * @union feature
 */
union feature {
	struct coordinate coord; ///< Coordinate
	uint32_t vector;         ///< Vector
};

/**
 * This is a function
 *
 * @param x The first thing
 * @param y The second thing
 *
 * @return {'Something': 'on success, -1 on error.'}
 */
int
foo(int x, int y);

/**
 * This is a function pointer prototype
 *
 * @param x The first thing
 * @param y The second thing
 *
 * @return {'Something': 'on success, -1 on error.'}
 */
typedef int (*binop_func)(int x, int y);

Pretty-printers

../../../../output/capi/libfoo_pp.h
/**
 * foo; Pretty Printer Implementation
 *
 * ------------------------------------------------------------------------------------------------
 * Copyright (C) Foo Bar <foo@example.com>
 * SPDX-License-Identifier: Unknown License
 *
 * @file libfoo_pp.h
 * ------------------------------------------------------------------------------------------------
 * NOTE: This file is created using yace: https://github.com/safl/yace
 */

/**
 * Options for pretty-printer (``*_pr``, ``*_fpr``) functions
 *
 * Options determines the format the pretty-printer uses, e.g. Yaml or JSON
 *
 * @enum foo_pr
 */
enum foo_pr {
	FOO_PR_DEF  = 0x0,
	FOO_PR_YAML = 0x1,
	FOO_PR_JSON = 0x2,
};

/**
 * Produces a string representation of the given ::plot_options
 *
 * @param enum_val the enum value to produce a string representation of
 *
 * @return On success, a string representation is returned. On error, the string
 * "ENOSYS" is returned.
 */
const char *
plot_options_str(int enum_val);

/**
 * Prints the given ::coordinate 'obj' to the given output 'stream'
 *
 * @param stream output stream used for printing
 * @param obj Pointer to the ::coordinate to print
 * @param flags Pretty-printer flags
 *
 * @return On success, the number of characters printed is returned.
 */
int
coordinate_fpr(FILE *stream, const struct coordinate *obj, int flags);

/**
 * Prints the given ::coordinate 'obj' to stdout
 *
 * @param obj Pointer to the ::coordinate to print
 * @param flags Pretty-printer flags
 *
 * @return On success, the number of characters printed is returned.
 */
int
coordinate_pr(const struct coordinate *obj, int flags);

/**
 * Prints the given ::feature 'obj' to the given output 'stream'
 *
 * @param stream output stream used for printing
 * @param obj Pointer to the ::feature to print
 * @param flags Pretty-printer flags
 *
 * @return On success, the number of characters printed is returned.
 */
int
feature_fpr(FILE *stream, const union feature *obj, int flags);

/**
 * Prints the given ::feature 'obj' to stdout
 *
 * @param obj Pointer to the ::feature to print
 * @param flags Pretty-printer flags
 *
 * @return On success, the number of characters printed is returned.
 */
int
feature_pr(const union feature *obj, int flags);
../../../../output/capi/foo_pp.c
/**
 * foo; Pretty Printer Implementation
 *
 * ------------------------------------------------------------------------------------------------
 * Copyright (C) Foo Bar <foo@example.com>
 * SPDX-License-Identifier: Unknown License
 *
 * @file foo_pp.c
 * ------------------------------------------------------------------------------------------------
 * NOTE: This file is created using yace: https://github.com/safl/yace
 */
#include <libfoo.h>
const char *
plot_options_str(int enum_val)
{
	switch (enum_val) {
	case PLOT_OPTIONS_PNG:
		return "PLOT_OPTIONS_PNG";
	case PLOT_OPTIONS_PDF:
		return "PLOT_OPTIONS_PDF";
	}

	return "ENOSYS";
}
static int
coordinate_yaml(FILE *stream, const struct coordinate *obj, int flags)
{
	int wrtn = 0;

	wrtn += fprintf(stream, "coordinate:");

	if (!obj) {
		wrtn += fprintf(stream, " ~\n");
		return wrtn;
	}

	wrtn += fprintf(stream, "\n");
	wrtn += fprintf(stream, "%*sx: %" PRIx32 "\n", 2, "", obj->x);
	wrtn += fprintf(stream, "%*sy: %" PRIx32 "\n", 2, "", obj->y);
	wrtn += fprintf(stream, "%*sz: %" PRIx32 "\n", 2, "", obj->z);

	return wrtn;
}

static int
coordinate_json(FILE *stream, const struct coordinate *obj, int flags)
{
	int wrtn = 0;

	if (!obj) {
		wrtn += fprintf(stream, "{ \"coordinate\": null }\n");
		return wrtn;
	}
	wrtn += fprintf(stream, "{\n%*s\"coordinate\": {\n", 2, "");
	wrtn += fprintf(stream, "%*s\"x\": %" PRIx32 "", 4, "", obj->x);
	wrtn += fprintf(stream, ",\n");

	wrtn += fprintf(stream, "%*s\"y\": %" PRIx32 "", 4, "", obj->y);
	wrtn += fprintf(stream, ",\n");

	wrtn += fprintf(stream, "%*s\"z\": %" PRIx32 "", 4, "", obj->z);
	wrtn += fprintf(stream, "\n");

	wrtn += fprintf(stream, "%*s}\n", 2, "");
	wrtn += fprintf(stream, "}\n");

	return wrtn;
}

int
coordinate_fpr(FILE *stream, const struct coordinate *obj, int flags)
{
	switch (flags) {
	case FOO_PR_DEF:
	case FOO_PR_YAML:
		return coordinate_yaml(stream, obj, flags);

	case FOO_PR_JSON:
		return coordinate_json(stream, obj, flags);
	}

	return -ENOSYS;
}

int
coordinate_pr(const struct coordinate *obj, int flags)
{
	return coordinate_fpr(stdout, obj, flags);
}
static int
feature_yaml(FILE *stream, const union feature *obj, int flags)
{
	int wrtn = 0;

	wrtn += fprintf(stream, "feature:");

	if (!obj) {
		wrtn += fprintf(stream, " ~\n");
		return wrtn;
	}

	wrtn += fprintf(stream, "\n");
	// What is this? field_decl
	wrtn += fprintf(stream, "%*svector: %" PRIx32 "\n", 2, "", obj->vector);

	return wrtn;
}

static int
feature_json(FILE *stream, const union feature *obj, int flags)
{
	int wrtn = 0;

	if (!obj) {
		wrtn += fprintf(stream, "{ \"feature\": null }\n");
		return wrtn;
	}
	wrtn += fprintf(stream, "{\n%*s\"feature\": {\n", 2, "");
	// What is this? field_declwrtn += fprintf(stream, ",\n" );

	wrtn += fprintf(stream, "%*s\"vector\": %" PRIx32 "", 4, "", obj->vector);
	wrtn += fprintf(stream, "\n");

	wrtn += fprintf(stream, "%*s}\n", 2, "");
	wrtn += fprintf(stream, "}\n");

	return wrtn;
}

int
feature_fpr(FILE *stream, const union feature *obj, int flags)
{
	switch (flags) {
	case FOO_PR_DEF:
	case FOO_PR_YAML:
		return feature_yaml(stream, obj, flags);

	case FOO_PR_JSON:
		return feature_json(stream, obj, flags);
	}

	return -ENOSYS;
}

int
feature_pr(const union feature *obj, int flags)
{
	return feature_fpr(stdout, obj, flags);
}

Check

../../../../output/capi/foo_check.c
/**
 * foo; this a simple check that pretty-printers behave as intended
 *
 * This is not an exhaustive test, however, ensuring that a program can be built using
 * the headers produced and that the generated pretty-printers are sound, goes a long
 * way.
 *
 * ------------------------------------------------------------------------------------------------
 * Copyright (C) Foo Bar <foo@example.com>
 * SPDX-License-Identifier: Unknown License
 *
 * @file foo_check.c
 * ------------------------------------------------------------------------------------------------
 * NOTE: This file is created using yace: https://github.com/safl/yace
 */
#include <stdio.h>
#include <libfoo.h>

int
test_coordinate_pr(void)
{
	struct coordinate obj = {0};
	int wrtn;

	printf("\n# output from: coordinate_pr(..., FOO_PR_YAML)\n");
	wrtn = coordinate_pr(&obj, FOO_PR_YAML);
	if (wrtn < 0) {
		return 1;
	}

	printf("\n# output from: coordinate_pr(..., FOO_PR_JSON)\n");
	wrtn = coordinate_pr(&obj, FOO_PR_JSON);
	if (wrtn < 0) {
		return 1;
	}

	return 0;
}

int
test_feature_pr(void)
{
	union feature obj = {0};
	int wrtn;

	printf("\n# output from: feature_pr(..., FOO_PR_YAML)\n");
	wrtn = feature_pr(&obj, FOO_PR_YAML);
	if (wrtn < 0) {
		return 1;
	}

	printf("\n# output from: feature_pr(..., FOO_PR_JSON)\n");
	wrtn = feature_pr(&obj, FOO_PR_JSON);
	if (wrtn < 0) {
		return 1;
	}

	return 0;
}

int
main(int argc, char *argv[])
{
	int err;

	printf("#\n");
	printf("# Pretty-printer output and testing\n");
	printf("#\n");

	err = test_coordinate_pr();
	if (err) {
		return err;
	}

	err = test_feature_pr();
	if (err) {
		return err;
	}

	return 0;
}

This foo_check.c is built using the Gcc tool, to check whether headers are well-behaved. Thus a file a.out exists which is executable result of translatning the source-file above.

When running it:

./output/capi/a.out

You can see textual representation of struct/union produced by the pretty-printers work:

#
# Pretty-printer output and testing
#

# output from: coordinate_pr(..., FOO_PR_YAML)
coordinate:
  x: 0
  y: 0
  z: 0

# output from: coordinate_pr(..., FOO_PR_JSON)
{
  "coordinate": {
    "x": 0,
    "y": 0,
    "z": 0
  }
}

# output from: feature_pr(..., FOO_PR_YAML)
feature:
  vector: 0

# output from: feature_pr(..., FOO_PR_JSON)
{
  "feature": {
    "vector": 0
  }
}

Example: Auxilary files

The auxilary files consists of:

  • Doxygen output HTML report output/capi/doxyreport/html/index.html

  • Clang-format style-files output/capi/clang-format*

  • Tool log-files output/capi/*.log

System Tools

The capi target uses the following tools:

To do what the tools does best, format the emitted code, produce Doxygen documentation project and build it, and lastly build the verification-program and run it.

Implementation

The CAPI emits a C API consisting of the following files:

  • lib{meta.prefix}.h – Library header bundle

  • lib{meta.prefix}_core.h – The core API declarations

  • lib{meta.prefix}_pp.h – Pretty-printer definitions

  • {meta.prefix}_pp.c – Pretty-printer implementation

additionally then:

  • {meta.prefix}_check.cmain() program checking generated code

This program is utilized by the ‘check-stage’ and is as such not part of the emitted C API but rather a test of it.

Last then:

  • doxygen.conf – Doxygen configuration for the above

Is emitted, this is to produce DoxyGen documentation for API.

The ‘format’ stage also emits the files:

  • hdr.clang-format – clang-format rule-file for header-files

  • src.clang-format – clang-format rule-file for source-files

Thus, the above files are what you should expect to see in the output-directory

class yace.targets.capi.target.CAPI(output)

Several helper functions

CFLAGS = ['-std=c11', '-pedantic-errors', '-Wall', '-Werror']
NAME = 'capi'
check()

Build generated sources and run the generated test-program

emit(model)

Emit code

format()

Transfer the clang-format definition from package to output and invokes the clang-formater

is_ready()

Pre-flight check, inspect availability of self.tools

transform(model)

Transform the given model

  • Transform symbols according to yace.transformation.CStyle

That it currently the only thing done to the yace IR.

yace.targets.capi.target.emit_cstr_fmt(typespec)
yace.targets.capi.target.emit_typespec(typespec, anon: bool = False)