Skip to content

Commit 860cbfa

Browse files
FelonEkonomclaudeNoarkhh
authored
Allow using Unifex enums and structs in NIF/Cnode state (#120)
* Allow using enums and structs in NIF/Cnode state * Update test fixtures * Add missing test fixtures * Fix dialyzer * Set version to 1.2.3 * Remove leftover * Add types tie header * Add special tie header for generated files * Drop separate types tie header Collapse the two tie headers introduced in c2a9153 / 4bcbdc7 back into a single `_generated/<name>.h` that includes both `<interface>/<name>.h` and `<interface>/<name>_types.h`. Document the typedef-include-struct pattern users follow to put custom types in their state. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Refactor typo * Update lib/unifex/code_generators/nif.ex * Fix pragma typo Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Jakub Pryc <94321002+Noarkhh@users.noreply.github.com>
1 parent d3917ca commit 860cbfa

41 files changed

Lines changed: 599 additions & 284 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lib/tasks/compile_unifex.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ defmodule Mix.Tasks.Compile.Unifex do
1818
|> Enum.each(fn {name, dir, specs_file} ->
1919
codes = specs_file |> Specs.parse(name) |> CodeGenerator.generate_code()
2020
Enum.each(codes, &InterfaceIO.store_interface!(name, dir, &1))
21-
generators = Enum.map(codes, fn {_header, _source, generator} -> generator end)
21+
22+
generators = codes |> Enum.map(& &1.generator)
23+
2224
tie_header = Unifex.CodeGenerator.TieHeader.generate_header(name, generators)
2325
InterfaceIO.store_tie_header!(name, dir, tie_header)
26+
2427
InterfaceIO.store_gitignore!(dir)
2528
end)
2629
end

lib/unifex/code_generator.ex

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ defmodule Unifex.CodeGenerator do
77

88
@type t :: module
99
@type code_t :: String.t()
10-
@type generated_code_t :: {header :: code_t, source :: code_t, generator :: t}
10+
@type generated_code_t :: %{header: code_t, types_header: code_t, source: code_t, generator: t}
1111

1212
@callback identification_constant() :: String.t()
1313
@callback interface_io_name() :: String.t()
14-
@callback generate_header(specs :: Specs.t()) :: code_t
14+
@callback generate_main_header(specs :: Specs.t()) :: code_t
15+
@callback generate_types_header(specs :: Specs.t()) :: code_t
1516
@callback generate_source(specs :: Specs.t()) :: code_t
1617

1718
@doc """
@@ -20,9 +21,16 @@ defmodule Unifex.CodeGenerator do
2021
@spec generate_code(Specs.t()) :: [generated_code_t()]
2122
def generate_code(specs) do
2223
for generator <- get_generators(specs) do
23-
header = generator.generate_header(specs)
24+
header = generator.generate_main_header(specs)
25+
types_header = generator.generate_types_header(specs)
2426
source = generator.generate_source(specs)
25-
{header, source, generator}
27+
28+
%{
29+
header: header,
30+
types_header: types_header,
31+
source: source,
32+
generator: generator
33+
}
2634
end
2735
end
2836

lib/unifex/code_generator/tie_header.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ defmodule Unifex.CodeGenerator.TieHeader do
1111
def generate_header(name, generators) do
1212
~g"""
1313
#pragma once
14-
1514
#{generate_includes(name, generators)}
1615
"""
1716
end
@@ -21,8 +20,11 @@ defmodule Unifex.CodeGenerator.TieHeader do
2120
end
2221

2322
defp generate_include(name, generator) do
23+
types_header_filename = Unifex.InterfaceIO.types_header_filename(name)
24+
2425
~g"""
2526
#ifdef #{generator.identification_constant()}
27+
#include "#{generator.interface_io_name()}/#{types_header_filename}"
2628
#include "#{generator.interface_io_name()}/#{name}.h"
2729
#endif
2830
"""

lib/unifex/code_generators/cnode.ex

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,12 @@ defmodule Unifex.CodeGenerators.CNode do
1717
def interface_io_name(), do: "cnode"
1818

1919
@impl CodeGenerator
20-
def generate_header(specs) do
20+
def generate_main_header(specs) do
2121
ctx = Common.create_ctx(specs)
2222

2323
~g"""
24-
#pragma once
25-
26-
#include <stdio.h>
27-
#include <stdint.h>
28-
#include <string.h>
29-
#include <stdlib.h>
30-
31-
// required for ei.h to work
32-
#ifndef _REENTRANT
33-
#define _REENTRANT
34-
#endif
35-
36-
#include <ei.h>
37-
#include <ei_connect.h>
38-
39-
#include <unifex/unifex.h>
40-
#include <unifex/cnode.h>
41-
#include <unifex/payload.h>
24+
#{pragma_and_includes()}
25+
#include "#{InterfaceIO.types_header_filename(specs.name)}"
4226
#include "#{InterfaceIO.user_header_path(specs.name)}"
4327
4428
#ifdef __cplusplus
@@ -47,13 +31,6 @@ defmodule Unifex.CodeGenerators.CNode do
4731
4832
#{generate_state_related_declarations(specs)}
4933
50-
#{Utils.generate_enums_definitions(specs.enums,
51-
&Common.generate_enum_native_definition/2,
52-
ctx)}
53-
54-
#{Utils.generate_structs_definitions(specs.structs,
55-
&generate_struct_native_definition/2,
56-
ctx)}
5734
5835
#{Utils.generate_functions_declarations(specs.functions_args,
5936
&generate_implemented_function_declaration/2,
@@ -75,12 +52,35 @@ defmodule Unifex.CodeGenerators.CNode do
7552
"""
7653
end
7754

55+
@impl CodeGenerator
56+
def generate_types_header(specs) do
57+
~g"""
58+
#pragma once
59+
60+
#ifdef __cplusplus
61+
extern "C" {
62+
#endif
63+
64+
#{Utils.generate_enums_definitions(specs.enums,
65+
&Common.generate_enum_native_definition/2,
66+
%{})}
67+
68+
#{Utils.generate_structs_definitions(specs.structs,
69+
&generate_struct_native_definition/2,
70+
%{})}
71+
72+
#ifdef __cplusplus
73+
}
74+
#endif
75+
"""
76+
end
77+
7878
@impl CodeGenerator
7979
def generate_source(specs) do
8080
ctx = Common.create_ctx(specs)
8181

8282
~g"""
83-
#include <stdio.h>
83+
#{pragma_and_includes()}
8484
#include "#{specs.name}.h"
8585
8686
#{generate_state_related_functions(specs)}
@@ -95,6 +95,29 @@ defmodule Unifex.CodeGenerators.CNode do
9595
"""
9696
end
9797

98+
defp pragma_and_includes() do
99+
~g"""
100+
#pragma once
101+
102+
#include <stdio.h>
103+
#include <stdint.h>
104+
#include <string.h>
105+
#include <stdlib.h>
106+
107+
// required for ei.h to work
108+
#ifndef _REENTRANT
109+
#define _REENTRANT
110+
#endif
111+
112+
#include <ei.h>
113+
#include <ei_connect.h>
114+
115+
#include <unifex/unifex.h>
116+
#include <unifex/cnode.h>
117+
#include <unifex/payload.h>
118+
"""
119+
end
120+
98121
defp generate_main_function_declaration(callbacks) do
99122
main = Map.get(callbacks, :main, "unifex_cnode_main_function")
100123
~g<int #{main}(int argc, char** argv);>

lib/unifex/code_generators/nif.ex

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,12 @@ defmodule Unifex.CodeGenerators.NIF do
1717
def interface_io_name(), do: "nif"
1818

1919
@impl CodeGenerator
20-
def generate_header(specs) do
20+
def generate_main_header(specs) do
2121
ctx = Common.create_ctx(specs)
2222

2323
~g"""
24-
#pragma once
25-
26-
#include <stdio.h>
27-
#include <stdint.h>
28-
#include <erl_nif.h>
29-
#include <unifex/unifex.h>
30-
#include <unifex/payload.h>
24+
#{pragma_and_includes()}
25+
#include "#{InterfaceIO.types_header_filename(specs.name)}"
3126
#include "#{InterfaceIO.user_header_path(specs.name)}"
3227
3328
#ifdef __cplusplus
@@ -42,14 +37,6 @@ defmodule Unifex.CodeGenerators.NIF do
4237
4338
#{generate_state_related_declarations(specs)}
4439
45-
#{Utils.generate_enums_definitions(specs.enums,
46-
&Common.generate_enum_native_definition/2,
47-
ctx)}
48-
49-
#{Utils.generate_structs_definitions(specs.structs,
50-
&generate_struct_native_definition/2,
51-
ctx)}
52-
5340
/*
5441
* Declaration of native functions for module #{specs.module}.
5542
* The implementation have to be provided by the user.
@@ -96,6 +83,43 @@ defmodule Unifex.CodeGenerators.NIF do
9683
"""
9784
end
9885

86+
@impl CodeGenerator
87+
def generate_types_header(specs) do
88+
ctx = Common.create_ctx(specs)
89+
90+
~g"""
91+
#{pragma_and_includes()}
92+
93+
#ifdef __cplusplus
94+
extern "C" {
95+
#endif
96+
97+
#{Utils.generate_enums_definitions(specs.enums,
98+
&Common.generate_enum_native_definition/2,
99+
ctx)}
100+
101+
#{Utils.generate_structs_definitions(specs.structs,
102+
&generate_struct_native_definition/2,
103+
ctx)}
104+
105+
#ifdef __cplusplus
106+
}
107+
#endif
108+
"""
109+
end
110+
111+
defp pragma_and_includes() do
112+
~g"""
113+
#pragma once
114+
115+
#include <stdio.h>
116+
#include <stdint.h>
117+
#include <erl_nif.h>
118+
#include <unifex/unifex.h>
119+
#include <unifex/payload.h>
120+
"""
121+
end
122+
99123
@impl CodeGenerator
100124
def generate_source(specs) do
101125
ctx = Common.create_ctx(specs)

lib/unifex/interface_IO.ex

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defmodule Unifex.InterfaceIO do
55

66
@spec_name_sufix ".spec.exs"
77
@generated_dir_name "_generated"
8+
@types_header_suffix "_types"
89

910
@spec user_header_path(Specs.native_name_t()) :: String.t()
1011
def user_header_path(name) do
@@ -34,7 +35,7 @@ defmodule Unifex.InterfaceIO do
3435
extension :: String.t()
3536
) :: String.t()
3637
def out_path(name, dir, generator, extension \\ "") do
37-
Path.join(out_dir(dir, generator), "#{name}#{extension}")
38+
out_dir(dir, generator) |> Path.join("#{name}#{extension}")
3839
end
3940

4041
@spec out_dir(base_dir :: String.t(), CodeGenerator.t()) :: String.t()
@@ -47,26 +48,24 @@ defmodule Unifex.InterfaceIO do
4748
dir :: String.t(),
4849
code :: CodeGenerator.generated_code_t()
4950
) :: :ok
50-
def store_interface!(name, dir, {header, source, generator}) do
51+
def store_interface!(name, dir, generated_code) do
52+
%{header: header, types_header: types_header, source: source, generator: generator} =
53+
generated_code
54+
5155
File.mkdir_p!(out_dir(dir, generator))
5256
out_base_path = out_path(name, dir, generator)
5357
File.write!("#{out_base_path}.h", header)
58+
File.write!("#{out_base_path}#{@types_header_suffix}.h", types_header)
5459
File.write!("#{out_base_path}.c", source)
5560
File.write!("#{out_base_path}.cpp", source)
5661

57-
# Format generated code only when clang-format is available
58-
if Unifex.Utils.clang_format_installed?() do
59-
System.cmd(
60-
"clang-format",
61-
[
62-
"-style={BasedOnStyle: llvm, IndentWidth: 2}",
63-
"-i",
64-
"#{out_base_path}.h",
65-
"#{out_base_path}.c",
66-
"#{out_base_path}.cpp"
67-
]
68-
)
69-
end
62+
:ok =
63+
run_clang_format_if_installed([
64+
"#{out_base_path}.h",
65+
"#{out_base_path}#{@types_header_suffix}.h",
66+
"#{out_base_path}.c",
67+
"#{out_base_path}.cpp"
68+
])
7069

7170
:ok
7271
end
@@ -77,10 +76,29 @@ defmodule Unifex.InterfaceIO do
7776
out_dir_name = Path.join(dir, @generated_dir_name)
7877
File.mkdir_p!(out_dir_name)
7978
out_base_path = Path.join(out_dir_name, "#{name}.h")
80-
File.write!(out_base_path, code)
79+
:ok = File.write!(out_base_path, code)
80+
:ok = run_clang_format_if_installed(out_base_path)
81+
82+
:ok
83+
end
84+
85+
defp run_clang_format_if_installed(files) when is_list(files) do
86+
if Unifex.Utils.clang_format_installed?() do
87+
System.cmd(
88+
"clang-format",
89+
[
90+
"-style={BasedOnStyle: llvm, IndentWidth: 2}",
91+
"-i"
92+
] ++ files
93+
)
94+
end
95+
8196
:ok
8297
end
8398

99+
defp run_clang_format_if_installed(file) when is_binary(file),
100+
do: run_clang_format_if_installed([file])
101+
84102
@spec store_gitignore!(String.t()) :: :ok
85103
def store_gitignore!(dir) do
86104
out_dir_name = Path.join(dir, @generated_dir_name)
@@ -94,4 +112,9 @@ defmodule Unifex.InterfaceIO do
94112

95113
:ok
96114
end
115+
116+
@spec types_header_filename(Specs.native_name_t()) :: String.t()
117+
def types_header_filename(name) do
118+
"#{name}#{@types_header_suffix}.h"
119+
end
97120
end

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Unifex.MixProject do
22
use Mix.Project
33

4-
@version "1.2.2"
4+
@version "1.2.3"
55
@github_url "https://github.com/membraneframework/unifex"
66

77
def project do

0 commit comments

Comments
 (0)