FIDL example: Calculator

This Calculator example shows how to construct a FIDL protocol with bare-minimum functionality. You will build on this example by modifying the methods to potentially return errors, composing a FIDL protocol with another, and showing usage of other primitives.

Getting started

In this example, you will create a basic calculator server & client which shows the fundamental setup needed to first define and then serve and consume a FIDL protocol.

First, you will define the interface definitions and test harness. The interface definition (the .fidl file itself) is the starting point for any new FIDL protocol. Additionally, the calculator includes the necessary CML and realm definitions to create a client-server pattern which can be used as project scaffolding for arbitrary implementations.

See below for the FIDL code:

FIDL

// Copyright 2022 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.

// The namespace for this FIDL protocol. This namespace is how both consumers (clients) and providers (servers) reference this protocol.
library examples.calculator.baseline;

// @discoverable indicates 'Calculator' is a protocol that will be served under the examples.calculator.baseline libarary namespace. https://fuchsia.dev/fuchsia-src/reference/fidl/language/attributes#discoverable . If @discoverable is missing, it will lead to a compile time error when trying to import the library.
@discoverable
// A limited-functionality calculator 'protocol' that adds and subtracts integers.
open protocol Calculator {
    // Takes as input a struct with two integers, and returns their sum: (a+b)=sum.  This method is infallible (no errors can be generated) as two int32's cannot overflow a result type of int64.
    flexible Add(struct {
        a int32;
        b int32;
    }) -> (struct {
        sum int64;
    });
    // Takes as input a struct with two integers, and returns their difference: (a-b)=difference.  This method is infallible (no errors can be generated) as two int32's cannot overflow a result type of int64.
    flexible Subtract(struct {
        a int32;
        b int32;
    }) -> (struct {
        difference int64;
    });
};

CML

Client

// Copyright 2022 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.
{
    include: [ "syslog/client.shard.cml" ],
    program: {
        runner: "elf",
        binary: "bin/client_bin",
    },
    use: [
        { protocol: "examples.calculator.baseline.Calculator" },
    ],
    config: {},
}

Server

// Copyright 2022 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.
{
    include: [ "syslog/client.shard.cml" ],
    program: {
        runner: "elf",
        binary: "bin/server_bin",
    },
    capabilities: [
        { protocol: "examples.calculator.baseline.Calculator" },
    ],
    expose: [
        {
            protocol: "examples.calculator.baseline.Calculator",
            from: "self",
        },
    ],
}

Realm

// Copyright 2022 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.
{
    children: [
        {
            name: "client",
            url: "#meta/client.cm",
        },
        {
            name: "server",
            url: "#meta/server.cm",
        },
    ],
    offer: [
        // Route the protocol under test from the server to the client.
        {
            protocol: "examples.calculator.baseline.Calculator",
            from: "#server",
            to: "#client",
        },

        // Route logging support to all children.
        {
            protocol: [
                "fuchsia.inspect.InspectSink",
                "fuchsia.logger.LogSink",
            ],
            from: "parent",
            to: [
                "#client",
                "#server",
            ],
        },
    ],
}

Client and server implementations can then be written in any supported language:

Rust

Client

// TODO(https://fxbug.dev/42063075): Rust implementation.

Server

// TODO(https://fxbug.dev/42063075): Rust implementation.

C++ (Natural)

Client

// TODO(https://fxbug.dev/42063075): C++ (Natural) implementation.

Server

// TODO(https://fxbug.dev/42063075): C++ (Natural) implementation.

C++ (Wire)

Client

// TODO(https://fxbug.dev/42063075): C++ (Wire) implementation.

Server

// TODO(https://fxbug.dev/42063075): C++ (Wire) implementation.

HLCPP

Client

// TODO(https://fxbug.dev/42063075): HLCPP implementation.

Server

// TODO(https://fxbug.dev/42063075): HLCPP implementation.

Creating a FIDL protocol from the ground up as is shown in this example can be a more common scenario for certain developers, such as platform developers. However, other types of developers also benefit from learning how to construct a FIDL protocol even if they won't typically do so. This helps you learn how everything about FIDL fits together, including the syntax, grammar, language features, how to serve and consume a given FIDL protocol, and how the build system works. For next steps, the examples which follow this baseline show how to extend an existing FIDL protocol, which is expected to be a fairly common practice.

Improving the design

Each of the following sections explores one potential way that you could iterate on the original design. Rather than building on one another sequentially, each presents an independent way in which the base case presented above may be modified or improved.