---
title: Exporting from Onyx
---

:source-highlighter: highlightjs

In Onyx, you may `export` a C entity from within an Onyx source file.
Later in the Onyx context, the entity is treated as if it was imported directly from a C header.

Unlike in raw C headers, an `export` statement is written in some mix of C with Onyx, allowing Onyx annotations, Onyx macros and having function bodies written in Onyx.

Normally, an `export` statement uses the ISO C syntax, and C vendor extensions support is undefined.
It is recommended to make use of Onyx annotations instead, which may be converted in accordance with a target vendor's semantics.
To achieve that, an Onyx implementation should maintain some sort of mapping between vendor C extensions and according Onyx semantics.

For example, FNX supports GCC extensions.
Upon generating a header file from Onyx source, the C vendor option (`-c[-w]`) is respected, and Onyx annotations are converted to the vendor's.

====
.main.nx
```
# A struct comment.
@[Pack]
export struct struct_t {
  // A variable comment.
  @[Align<8>]
  int foo;

  double bar;
};
```

After running `fnx doc -fh -c[-wgnu] main.nx`:

.main.h
```c
// A struct comment.
struct __attribute__ ((__packed__)) struct_t {
  // My comment.
  int foo __attribute__ ((aligned (8)));

  double bar;
}
```
====

A C macro is treated as a freestanding entity, hence requiring a separate `export` entity per macro, for example:

====
.main.nx
```nx
export #ifdef __GNUC__
@[AlwaysInline]
export int foo();
export #elif _MSC_VER
@[AlwaysInline]
export double foo();
export #endif
```

After running `fnx doc -fh -c[-wgnu] main.nx`:

.main.h
```c
#ifdef __GNUC__
__attribute__((always_inline))
int foo();
#elif _MSC_VER
__attribute__((always_inline))
double foo();
#endif
```

Note that both annotations have expanded to GCC attributes regardless of the `_MSC_VER` macro, because the behaviour is controlled by the `-c[-w]` option.
To solve that, you either want to use explicit C annotations:

.main.nx
```nx
export #ifdef __GNUC__
export __attribute__((always_inline)) int foo();
export #elif _MSC_VER
export __forceinline double foo();
export #endif
```

Or expand them in macros (which is equivalent to writing them explicitly):

.main.nx
```nx
macro attr(vendor)
  {{ nx.id["AlwaysInline"]:c(vendor) }}
end

export #ifdef __GNUC__
export @attr("gnu") int foo();
export #elif _MSC_VER
export @attr("msvc") double foo();
export #endif
```
====

Sometimes it may become cumbersome to have a separate `export` statement per entity, like in the examples above.
To deal with that, Onyx allows to export entire blocks in the same C-NX syntax.
The same rules apply.

====
.main.nx
```nx
export {
#ifdef __GNUC__
  @[AlwaysInline]
  int foo();
#elif _MSC_VER
  @[AlwaysInline]
  double foo();
#endif

  // A C comment
  @[NoInline]
  void main() {
    # Some Onyx code
  }
}
```

After running `fnx doc -fh -c[-wgnu] main.nx`:

.main.h
```c
#ifdef __GNUC__
__attribute__((always_inline))
int foo();
#elif _MSC_VER
__attribute__((always_inline))
double foo();
#endif

// A C comment
void main() __attribute__((noinline));
```
====

It is common to have entire files written in this manner when tight interoperability with C is implied, usually with an `.cnx` extension.
