Skip to content

Instantly share code, notes, and snippets.

@ChrisRackauckas
Created July 26, 2025 14:48
Show Gist options
  • Select an option

  • Save ChrisRackauckas/181569d8c3ec892b29dda48359abf865 to your computer and use it in GitHub Desktop.

Select an option

Save ChrisRackauckas/181569d8c3ec892b29dda48359abf865 to your computer and use it in GitHub Desktop.
Julia @ccallable C Header Generator - Automatically generate C header files from Julia functions marked with @ccallable

Julia @ccallable C Header Generator

This collection of Julia scripts automatically generates C header files from Julia functions marked with the @ccallable macro. This is useful when creating Julia libraries that need to be called from C/C++ code.

Files Description

1. ccallable_header_generator.jl

The main generator script that provides a simple and robust approach to generating C headers. It uses a manual definition approach where you explicitly define function signatures, avoiding complex AST parsing.

Features:

  • Direct type mapping from Julia to C types
  • Support for pointers, arrays, and basic types
  • Generates complete C headers with guards and C++ compatibility
  • Includes a convenient @ccfunc macro for defining signatures

2. generate_c_header.jl

The original generator with comprehensive type mappings. This script provides the core functionality for converting Julia types to their C equivalents.

Features:

  • Extensive Julia-to-C type mapping dictionary
  • Support for standard C types (int, float, pointers, etc.)
  • Handles arrays as opaque pointers
  • Generates C++ compatible headers

3. parse_ccallable.jl

Parser for extracting @ccallable macro signatures from Julia source code. This script can analyze Julia files and extract function information automatically.

Features:

  • Parses Julia AST to find @ccallable functions
  • Extracts function names, return types, and arguments
  • Supports both file and string parsing
  • Handles various @ccallable syntax forms

4. example_usage.jl

Complete example demonstrating the functionality of the header generator with a sample Julia library.

Demonstrates:

  • Various @ccallable function patterns
  • Mathematical operations
  • Array and string handling
  • Error handling patterns
  • Complex number operations

Usage

Method 1: Manual Definition (Recommended)

include("ccallable_header_generator.jl")

# Define your functions
functions = [
    CCFunction("add_numbers", "Cint", [("a", "Cint"), ("b", "Cint")]),
    CCFunction("process_array", "Cvoid", [("data", "Ptr{Cdouble}"), ("size", "Csize_t")]),
    CCFunction("get_version", "Cstring", []),
]

# Generate header
generate_c_header(
    functions, 
    "mylib.h",
    header_guard="MYLIB_H",
    includes=["<string.h>"],
    comment="C API for My Julia Library"
)

Method 2: Automatic Parsing

include("parse_ccallable.jl")

# Parse Julia source file
functions = extract_ccallable_from_file("mylib.jl")

# Or parse from string
julia_code = """
@ccallable function compute_sum(x::Cint, y::Cint)::Cint
    return x + y
end
"""
functions = extract_ccallable_from_string(julia_code)

# Generate header
generate_header_file(functions, "mylib.h", header_guard="MYLIB_H")

Type Mappings

The scripts support the following Julia to C type mappings:

Julia Type C Type
Int8 int8_t
UInt8 uint8_t
Int16 int16_t
UInt16 uint16_t
Int32 int32_t
UInt32 uint32_t
Int64 int64_t
UInt64 uint64_t
Float32 float
Float64 double
Bool bool
Cint int
Cdouble double
Cstring const char*
Ptr{T} T*
Cvoid void
Nothing void

Example Output

Given a Julia function:

@ccallable function mylib_add(a::Cdouble, b::Cdouble)::Cdouble
    return a + b
end

The generator produces:

double mylib_add(double a, double b);

Complete Workflow

  1. Write Julia functions with @ccallable:

    @ccallable function mylib_init()::Cint
        # Initialize library
        return 0
    end
  2. Generate C header:

    julia parse_ccallable.jl mylib.jl
  3. Use in C code:

    #include "mylib.h"
    
    int main() {
        if (mylib_init() != 0) {
            return 1;
        }
        // Use other functions...
        return 0;
    }
  4. Compile with juliac or Julia embedding:

    gcc -o myapp main.c -L. -lmylib -ljulia

Notes

  • The @ccallable macro is used to mark Julia functions that should be callable from C
  • All arguments and return types must be C-compatible types
  • Arrays are passed as pointers with separate length parameters
  • Strings use Cstring (null-terminated) or Ptr{Cchar} for buffers
  • Error handling typically uses integer return codes

Requirements

  • Julia 1.0 or later
  • No external dependencies required
#!/usr/bin/env julia
"""
Simple and robust C header generator for Julia @ccallable functions.
This script provides a manual approach where you define the function signatures
directly, avoiding complex AST parsing.
"""
# Type mapping from Julia to C types
const JULIA_TO_C_TYPE = Dict{String, String}(
"Int8" => "int8_t",
"UInt8" => "uint8_t",
"Int16" => "int16_t",
"UInt16" => "uint16_t",
"Int32" => "int32_t",
"UInt32" => "uint32_t",
"Int64" => "int64_t",
"UInt64" => "uint64_t",
"Int" => "intptr_t",
"UInt" => "uintptr_t",
"Float32" => "float",
"Float64" => "double",
"Cdouble" => "double",
"Cfloat" => "float",
"Bool" => "bool",
"Cchar" => "char",
"Cint" => "int",
"Cuint" => "unsigned int",
"Clong" => "long",
"Culong" => "unsigned long",
"Csize_t" => "size_t",
"Cvoid" => "void",
"Ptr{Cvoid}" => "void*",
"Ptr{UInt8}" => "uint8_t*",
"Ptr{Int8}" => "int8_t*",
"Ptr{Cchar}" => "char*",
"Ptr{Cdouble}" => "double*",
"Ptr{Cfloat}" => "float*",
"Ptr{Cint}" => "int*",
"Ptr{Float64}" => "double*",
"Ptr{Float32}" => "float*",
"Ptr{Int32}" => "int32_t*",
"Ptr{Int64}" => "int64_t*",
"Cstring" => "const char*",
"Nothing" => "void",
)
"""
CCFunction
Simplified representation of a C-callable function.
"""
struct CCFunction
name::String
return_type::String # Julia type as string
args::Vector{Tuple{String, String}} # (name, julia_type)
end
"""
julia_type_to_c(jl_type::String)
Convert a Julia type string to its C equivalent.
"""
function julia_type_to_c(jl_type::String)
if haskey(JULIA_TO_C_TYPE, jl_type)
return JULIA_TO_C_TYPE[jl_type]
else
@warn "Unknown type mapping for $jl_type, using void*"
return "void*"
end
end
"""
generate_c_declaration(func::CCFunction)
Generate a C function declaration from a CCFunction.
"""
function generate_c_declaration(func::CCFunction)
# Convert return type
ret_type = julia_type_to_c(func.return_type)
# Convert arguments
arg_decls = String[]
for (arg_name, arg_type) in func.args
c_type = julia_type_to_c(arg_type)
push!(arg_decls, "$c_type $arg_name")
end
# Handle empty argument list
arg_list = isempty(arg_decls) ? "void" : join(arg_decls, ", ")
return "$ret_type $(func.name)($arg_list);"
end
"""
generate_c_header(functions::Vector{CCFunction}, output_file::String;
header_guard::String="", includes::Vector{String}=String[],
comment::String="")
Generate a complete C header file with the given functions.
"""
function generate_c_header(functions::Vector{CCFunction}, output_file::String;
header_guard::String="",
includes::Vector{String}=String[],
comment::String="")
# Generate header guard if not provided
if isempty(header_guard)
header_guard = uppercase(replace(basename(output_file), r"[^A-Za-z0-9]" => "_"))
end
open(output_file, "w") do io
# Header comment
if !isempty(comment)
println(io, "/*")
for line in split(comment, '\n')
println(io, " * $line")
end
println(io, " */")
println(io)
end
# Header guard
println(io, "#ifndef $header_guard")
println(io, "#define $header_guard")
println(io)
# Standard includes
println(io, "#include <stdint.h>")
println(io, "#include <stdbool.h>")
println(io, "#include <stddef.h>")
# Custom includes
for inc in includes
println(io, "#include $inc")
end
println(io)
# C++ compatibility
println(io, "#ifdef __cplusplus")
println(io, "extern \"C\" {")
println(io, "#endif")
println(io)
# Function declarations
for func in functions
println(io, generate_c_declaration(func))
end
println(io)
println(io, "#ifdef __cplusplus")
println(io, "}")
println(io, "#endif")
println(io)
println(io, "#endif /* $header_guard */")
end
end
# Convenience function to define a @ccallable function signature
macro ccfunc(name, return_type, args...)
args_vec = Tuple{String, String}[]
for arg in args
if isa(arg, Expr) && arg.head == :(::) && length(arg.args) == 2
push!(args_vec, (string(arg.args[1]), string(arg.args[2])))
else
error("Invalid argument format. Expected name::Type")
end
end
return CCFunction(string(name), string(return_type), args_vec)
end
# Example usage
if abspath(PROGRAM_FILE) == @__FILE__
# Define your @ccallable functions
functions = [
# Basic arithmetic
CCFunction("add_numbers", "Cint", [("a", "Cint"), ("b", "Cint")]),
CCFunction("multiply_doubles", "Cdouble", [("x", "Cdouble"), ("y", "Cdouble")]),
# Array operations
CCFunction("sum_array", "Cdouble", [("arr", "Ptr{Cdouble}"), ("length", "Csize_t")]),
CCFunction("process_buffer", "Cvoid", [("data", "Ptr{UInt8}"), ("size", "Csize_t")]),
# String operations
CCFunction("get_version", "Cstring", []),
CCFunction("concat_strings", "Cint", [
("str1", "Cstring"),
("str2", "Cstring"),
("result", "Ptr{Cchar}"),
("buffer_size", "Csize_t")
]),
# Matrix operations
CCFunction("matrix_multiply", "Cint", [
("A", "Ptr{Cdouble}"),
("B", "Ptr{Cdouble}"),
("C", "Ptr{Cdouble}"),
("m", "Csize_t"),
("n", "Csize_t"),
("k", "Csize_t")
]),
# Initialization/cleanup
CCFunction("init_library", "Cint", []),
CCFunction("cleanup_library", "Cvoid", []),
]
# You can also use the macro syntax (commented out to avoid eval issues)
# push!(functions, @ccfunc(compute_norm, Cdouble, vec::Ptr{Cdouble}, n::Csize_t))
# Generate the header file
generate_c_header(
functions,
"julia_api.h",
header_guard="JULIA_API_H",
includes=["<string.h>", "<math.h>"],
comment="""Generated C API for Julia @ccallable functions
This header provides C declarations for functions exported from Julia
using the @ccallable macro."""
)
println("Generated julia_api.h")
println("\nContent:")
println("=" ^ 70)
println(read("julia_api.h", String))
println("=" ^ 70)
# Generate corresponding Julia implementation template
println("\nCorresponding Julia implementation template:")
println("=" ^ 70)
for func in functions
print("@ccallable function $(func.name)(")
args = ["$(arg[1])::$(arg[2])" for arg in func.args]
print(join(args, ", "))
println(")::$(func.return_type)")
println(" # TODO: Implement")
println(" $(func.return_type == "Cvoid" ? "nothing" : "return 0")")
println("end")
println()
end
end
#!/usr/bin/env julia
"""
Example demonstrating how to use the C header generator with @ccallable functions.
"""
include("parse_ccallable.jl")
# Example 1: Simple Julia file with @ccallable functions
example_julia_code = """
module MyCLibrary
using Base: @ccallable
# Initialize the library
@ccallable function mylib_init()::Cint
println("Library initialized")
return 0
end
# Cleanup the library
@ccallable function mylib_cleanup()::Cvoid
println("Library cleaned up")
nothing
end
# Mathematical operations
@ccallable function mylib_add(a::Cdouble, b::Cdouble)::Cdouble
return a + b
end
@ccallable function mylib_multiply_matrices(
A::Ptr{Cdouble},
B::Ptr{Cdouble},
C::Ptr{Cdouble},
m::Csize_t,
n::Csize_t,
k::Csize_t
)::Cint
# Matrix multiplication: C = A * B
# A is m×k, B is k×n, C is m×n
try
for i in 1:m
for j in 1:n
sum = 0.0
for l in 1:k
sum += unsafe_load(A, (i-1)*k + l) * unsafe_load(B, (l-1)*n + j)
end
unsafe_store!(C, sum, (i-1)*n + j)
end
end
return 0 # Success
catch
return -1 # Error
end
end
# String operations
@ccallable function mylib_concat_strings(
str1::Cstring,
str2::Cstring,
result::Ptr{Cchar},
buffer_size::Csize_t
)::Cint
s1 = unsafe_string(str1)
s2 = unsafe_string(str2)
concatenated = s1 * s2
if length(concatenated) + 1 > buffer_size
return -1 # Buffer too small
end
for (i, c) in enumerate(concatenated)
unsafe_store!(result, c, i)
end
unsafe_store!(result, '\0', length(concatenated) + 1)
return 0
end
# Array operations
@ccallable function mylib_sum_array(arr::Ptr{Cint}, length::Csize_t)::Cint
sum = 0
for i in 1:length
sum += unsafe_load(arr, i)
end
return sum
end
# Complex number operations (using two doubles)
@ccallable function mylib_complex_multiply(
real1::Cdouble, imag1::Cdouble,
real2::Cdouble, imag2::Cdouble,
result_real::Ptr{Cdouble}, result_imag::Ptr{Cdouble}
)::Cvoid
# (a + bi) * (c + di) = (ac - bd) + (ad + bc)i
real_part = real1 * real2 - imag1 * imag2
imag_part = real1 * imag2 + imag1 * real2
unsafe_store!(result_real, real_part)
unsafe_store!(result_imag, imag_part)
nothing
end
# Error handling example
@ccallable function mylib_safe_divide(a::Cdouble, b::Cdouble, result::Ptr{Cdouble})::Cint
if b == 0.0
return -1 # Division by zero error
end
unsafe_store!(result, a / b)
return 0 # Success
end
end # module
"""
# Parse the Julia code
println("Parsing Julia code for @ccallable functions...")
functions = extract_ccallable_from_string(example_julia_code)
println("\nFound $(length(functions)) @ccallable functions:")
for (i, func) in enumerate(functions)
print(" $i. $(func.name)(")
args = ["$(name)::$(julia_type_to_c(type))" for (name, type) in zip(func.arg_names, func.arg_types)]
print(join(args, ", "))
println(") -> $(julia_type_to_c(func.return_type))")
end
# Generate the C header file
output_file = "mylib.h"
println("\nGenerating C header file: $output_file")
generate_header_file(
functions,
output_file,
header_guard="MYLIB_H",
includes=["<string.h>", "<math.h>"]
)
# Display the generated header
println("\nGenerated C header file content:")
println("=" * 70)
println(read(output_file, String))
println("=" * 70)
# Create a sample C program that uses the generated header
c_example = """
#include "mylib.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
// Initialize the library
if (mylib_init() != 0) {
fprintf(stderr, "Failed to initialize library\\n");
return 1;
}
// Test arithmetic operations
double sum = mylib_add(3.14, 2.86);
printf("3.14 + 2.86 = %.2f\\n", sum);
// Test array operations
int numbers[] = {1, 2, 3, 4, 5};
int array_sum = mylib_sum_array(numbers, 5);
printf("Sum of [1,2,3,4,5] = %d\\n", array_sum);
// Test safe division
double result;
if (mylib_safe_divide(10.0, 3.0, &result) == 0) {
printf("10.0 / 3.0 = %.2f\\n", result);
}
// Test complex multiplication
double real_result, imag_result;
mylib_complex_multiply(3.0, 4.0, 1.0, 2.0, &real_result, &imag_result);
printf("(3+4i) * (1+2i) = %.2f + %.2fi\\n", real_result, imag_result);
// Cleanup
mylib_cleanup();
return 0;
}
"""
println("\nExample C program using the generated header:")
println("=" ^ 70)
println(c_example)
println("=" ^ 70)
# Save the C example
open("example_main.c", "w") do io
write(io, c_example)
end
println("\nC example saved to: example_main.c")
println("\nTo compile with Julia's C-callable functions:")
println(" 1. Build your Julia code as a shared library using juliac")
println(" 2. Compile: gcc -o example example_main.c -L. -lmylib -ljulia")
println(" 3. Run: ./example")
#!/usr/bin/env julia
"""
Generate C header files from Julia @ccallable function signatures.
This script parses Julia functions marked with @ccallable and generates
corresponding C header declarations.
"""
using Base: return_types
# Type mapping from Julia to C types
const JULIA_TO_C_TYPE = Dict{Type, String}(
Int8 => "int8_t",
UInt8 => "uint8_t",
Int16 => "int16_t",
UInt16 => "uint16_t",
Int32 => "int32_t",
UInt32 => "uint32_t",
Int64 => "int64_t",
UInt64 => "uint64_t",
Int => "intptr_t",
UInt => "uintptr_t",
Float32 => "float",
Float64 => "double",
Bool => "bool",
Cchar => "char",
Cint => "int",
Cuint => "unsigned int",
Clong => "long",
Culong => "unsigned long",
Csize_t => "size_t",
Cvoid => "void",
Ptr{Cvoid} => "void*",
Ptr{UInt8} => "uint8_t*",
Ptr{Int8} => "int8_t*",
Ptr{Cchar} => "char*",
Cstring => "const char*",
Nothing => "void",
)
"""
CCallableFunction
Represents a C-callable function with its signature information.
"""
struct CCallableFunction
name::String
return_type::Type
arg_types::Vector{Type}
arg_names::Vector{String}
end
"""
julia_type_to_c(jl_type::Type)
Convert a Julia type to its C equivalent string representation.
"""
function julia_type_to_c(jl_type::Type)
if haskey(JULIA_TO_C_TYPE, jl_type)
return JULIA_TO_C_TYPE[jl_type]
elseif jl_type <: Ptr
element_type = eltype(jl_type)
if haskey(JULIA_TO_C_TYPE, element_type)
return JULIA_TO_C_TYPE[element_type] * "*"
else
return "void*" # Default pointer type
end
elseif jl_type <: Ref
element_type = eltype(jl_type)
if haskey(JULIA_TO_C_TYPE, element_type)
return JULIA_TO_C_TYPE[element_type] * "*"
else
return "void*" # Default pointer type
end
else
# Handle arrays and other complex types
if jl_type <: AbstractArray
return "void*" # Arrays passed as opaque pointers
else
@warn "Unknown type mapping for $jl_type, using void*"
return "void*"
end
end
end
"""
generate_c_declaration(func::CCallableFunction)
Generate a C function declaration from a CCallableFunction.
"""
function generate_c_declaration(func::CCallableFunction)
# Convert return type
ret_type = julia_type_to_c(func.return_type)
# Convert argument types
arg_decls = String[]
for (i, (arg_type, arg_name)) in enumerate(zip(func.arg_types, func.arg_names))
c_type = julia_type_to_c(arg_type)
push!(arg_decls, "$c_type $arg_name")
end
# Handle empty argument list
if isempty(arg_decls)
arg_list = "void"
else
arg_list = join(arg_decls, ", ")
end
return "$ret_type $(func.name)($arg_list);"
end
"""
generate_header_file(functions::Vector{CCallableFunction},
output_file::String;
header_guard::String="",
includes::Vector{String}=String[])
Generate a complete C header file with the given functions.
"""
function generate_header_file(functions::Vector{CCallableFunction},
output_file::String;
header_guard::String="",
includes::Vector{String}=String[])
# Generate header guard if not provided
if isempty(header_guard)
header_guard = uppercase(replace(basename(output_file), r"[^A-Za-z0-9]" => "_"))
end
open(output_file, "w") do io
# Header guard
println(io, "#ifndef $header_guard")
println(io, "#define $header_guard")
println(io)
# Standard includes
println(io, "#include <stdint.h>")
println(io, "#include <stdbool.h>")
println(io, "#include <stddef.h>")
# Custom includes
for inc in includes
println(io, "#include $inc")
end
println(io)
# C++ compatibility
println(io, "#ifdef __cplusplus")
println(io, "extern \"C\" {")
println(io, "#endif")
println(io)
# Function declarations
for func in functions
println(io, generate_c_declaration(func))
end
println(io)
println(io, "#ifdef __cplusplus")
println(io, "}")
println(io, "#endif")
println(io)
println(io, "#endif /* $header_guard */")
end
end
# Example usage demonstrating how to use the generator
if abspath(PROGRAM_FILE) == @__FILE__
# Example @ccallable functions that would be defined in Julia
# These represent typical patterns found in Julia C-callable code
example_functions = [
CCallableFunction("add_numbers", Int32, [Int32, Int32], ["a", "b"]),
CCallableFunction("process_array", Cvoid, [Ptr{Float64}, Csize_t], ["data", "length"]),
CCallableFunction("get_string", Cstring, [Cint], ["id"]),
CCallableFunction("matrix_multiply", Cint, [Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, Csize_t, Csize_t, Csize_t],
["a", "b", "result", "m", "n", "k"]),
CCallableFunction("init_library", Cvoid, Type[], String[]),
CCallableFunction("cleanup_library", Cvoid, Type[], String[]),
]
# Generate the header file
generate_header_file(example_functions, "julia_functions.h",
header_guard="JULIA_FUNCTIONS_H",
includes=["<string.h>"])
println("Generated julia_functions.h")
# Show the generated content
println("\nGenerated header content:")
println("=" ^ 60)
println(read("julia_functions.h", String))
end
#!/usr/bin/env julia
"""
Parser for @ccallable macro signatures to extract function information
for C header generation.
"""
using Base.Meta
include("generate_c_header.jl")
"""
parse_ccallable_signature(expr)
Parse a @ccallable macro expression and extract function information.
Returns a CCallableFunction or nothing if parsing fails.
"""
function parse_ccallable_signature(expr)
# Handle different forms of @ccallable
# Form 1: @ccallable function name(args...)::RetType ... end
# Form 2: @ccallable name(args...)::RetType = ...
if isa(expr, Expr) && expr.head == :macrocall
# Check if this is a @ccallable macro
macro_name = expr.args[1]
if isa(macro_name, Symbol) && string(macro_name) == "@ccallable"
# Extract the actual function expression
# In Julia 1.x, macro expressions have the form: (:macrocall, Symbol("@name"), LineNumberNode, actual_expr)
func_expr = length(expr.args) >= 3 ? expr.args[end] : nothing
if func_expr === nothing
return nothing
end
return parse_function_signature(func_expr)
elseif isa(macro_name, Expr) && macro_name.head == :. &&
length(macro_name.args) >= 2 && macro_name.args[2] == QuoteNode(:ccallable)
# Handle Base.@ccallable or similar qualified macro calls
func_expr = length(expr.args) >= 3 ? expr.args[end] : nothing
if func_expr === nothing
return nothing
end
return parse_function_signature(func_expr)
end
end
return nothing
end
"""
parse_function_signature(expr)
Parse a function definition and extract signature information.
"""
function parse_function_signature(expr)
if isa(expr, Expr)
if expr.head == :function || expr.head == :(=)
# Extract function signature
sig_expr = expr.head == :function ? expr.args[1] : expr.args[1]
if isa(sig_expr, Expr) && sig_expr.head == :(::)
# Has return type annotation
call_expr = sig_expr.args[1]
ret_type = try
eval(sig_expr.args[2])
catch
# If we can't evaluate the type, store it as a symbol for later
sig_expr.args[2]
end
else
# No return type annotation, assume Cvoid
call_expr = sig_expr
ret_type = Cvoid
end
if isa(call_expr, Expr) && call_expr.head == :call
func_name = string(call_expr.args[1])
# Parse arguments
arg_types = Type[]
arg_names = String[]
for i in 2:length(call_expr.args)
arg = call_expr.args[i]
if isa(arg, Expr) && arg.head == :(::)
# Typed argument
push!(arg_names, string(arg.args[1]))
push!(arg_types, try
eval(arg.args[2])
catch
arg.args[2]
end)
elseif isa(arg, Symbol)
# Untyped argument - this shouldn't happen for @ccallable
@warn "Untyped argument in @ccallable function: $arg"
push!(arg_names, string(arg))
push!(arg_types, Any)
end
end
return CCallableFunction(func_name, ret_type, arg_types, arg_names)
end
end
end
return nothing
end
"""
extract_ccallable_from_file(filename::String)
Extract all @ccallable function signatures from a Julia source file.
"""
function extract_ccallable_from_file(filename::String)
content = read(filename, String)
expressions = Meta.parse("begin\n$content\nend").args
functions = CCallableFunction[]
for expr in expressions
func = parse_ccallable_signature(expr)
if func !== nothing
push!(functions, func)
end
end
return functions
end
"""
extract_ccallable_from_string(code::String)
Extract all @ccallable function signatures from a Julia code string.
"""
function extract_ccallable_from_string(code::String)
expressions = Meta.parse("begin\n$code\nend").args
functions = CCallableFunction[]
for expr in expressions
func = parse_ccallable_signature(expr)
if func !== nothing
push!(functions, func)
end
end
return functions
end
# Example usage and testing
if abspath(PROGRAM_FILE) == @__FILE__
# Test parsing various @ccallable signatures
test_code = """
# Standard @ccallable function
@ccallable function compute_sum(x::Cint, y::Cint)::Cint
return x + y
end
# @ccallable with pointer arguments
@ccallable function process_buffer(data::Ptr{UInt8}, size::Csize_t)::Cvoid
# Process data
nothing
end
# @ccallable returning a string
@ccallable function get_version()::Cstring
return Base.unsafe_convert(Cstring, "1.0.0")
end
# @ccallable with float operations
@ccallable function dot_product(a::Ptr{Float64}, b::Ptr{Float64}, n::Csize_t)::Float64
result = 0.0
for i in 1:n
result += unsafe_load(a, i) * unsafe_load(b, i)
end
return result
end
# @ccallable with no arguments
@ccallable function initialize()::Cint
# Initialize library
return 0
end
"""
# Parse the test code
functions = extract_ccallable_from_string(test_code)
println("Found $(length(functions)) @ccallable functions:")
for func in functions
println(" - $(func.name)")
end
# Generate header file
generate_header_file(functions, "test_output.h",
header_guard="TEST_OUTPUT_H")
println("\nGenerated test_output.h:")
println("=" ^ 60)
println(read("test_output.h", String))
end
@vladaviedov
Copy link

Hi! What license (if any) are the scripts under?

@ChrisRackauckas
Copy link
Author

Treat it as MIT. It's Claude-generated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment