Export enum

Hi

I am defining an enum such as @enum myenum enumname1 enumname2 in a module using Julia 0.6. it seems I have to explicitly export both enum type name(myenum) and enum elements(enumname1,enumname2). I know this way is more explicit, but usually we want all elements of an enum type be exported too. is there a way to just do export myenum so that type name and all its elements be exported, or should this behavior be the default for export enum?

Alex

2 Likes

Here’s a macro that does it:

macro exported_enum(name, args...)
    esc(quote
        @enum($name, $(args...))
        export $name
        $([:(export $arg) for arg in args]...)
        end)
end

@exported_enum fruit apple banana
7 Likes

this macro is great, should be included in the base. thanks sharing.

1 Like

In order to deal with the case where you specify the value of each instances, you may use:

julia> module A
           macro exported_enum(T, syms...)
               return esc(quote
                   @enum($T, $(syms...))
                   export $T
                   for inst in Symbol.(instances($T))
                       eval($(Expr(:quote, :(export $(Expr(:$, :inst))))))
                   end
               end)
           end
           @exported_enum fruit apple=2 banana=3
       end
Main.A

julia> using Main.A

julia> fruit
Enum fruit:
apple = 2
banana = 3
2 Likes

sorry for reviving this.
I got the macro to to support a begin end block (with comments)
(I had some help from the ai, as macro definition syntax is a bit of a black art)
eg.

@exported_enum MyEnum begin
    #this is a
    A
    #this is b
    B
end

macro exported_enum(name, args...)
    if length(args) == 1 && args[1] isa Expr && args[1].head == :block
        # Handle the begin ... end block syntax
        block = args[1] 
        # Filter out comments from the block
        enum_members = filter(x -> x isa Symbol, block.args)
        return esc(quote
            @enum $name begin
                $(enum_members...)
            end
            export $name
            $([:(export $arg) for arg in enum_members]...)
        end)
    else
        # Handle the standard @enum syntax
        return esc(quote
            @enum($name, $(args...))
            export $name
            $([:(export $arg) for arg in args]...)
        end)
    end
end
1 Like

Still this only working for symbols, not expression. Gemini gave me out this. Don’t know how correct it is, for me is working:

"""
    @export_enum_members(EnumType)

Exports all instances (members) of a previously defined enum type from the current module.

This macro automates the process of writing individual `export` statements for each
member of an enum. It is particularly useful for enums with many members or for
maintaining a clean separation between definition and export logic.

Note: This macro only exports the enum's instances; it does **not** export the enum
type itself.

# Usage

First, define your enum as you normally would. Then, call the macro with the name
of the enum type.

```julia
# 1. Define the enum
@enum MyColor begin
    RED
    GREEN
    BLUE
end

# 2. Export only its members using the macro
@export_enum_members MyColor
```

# Examples

#### Standard Use Case:

This is the intended use of the macro.

```julia
module MyDrawingApp

# Define the enum internally
@enum ToolType begin
    PENCIL
    BRUSH
    ERASER
end

# Make the tools available to users, but keep the `ToolType` abstract.
# The user can use `PENCIL`, but not necessarily the type `ToolType`.
@export_enum_members ToolType

end
```

The macro call `@export_enum_members ToolType` automatically expands to the
following code during compilation:
```julia
export PENCIL
export BRUSH
export ERASER
```

#### Comparison with `@exported_enum`:

This macro differs from a combined-definition macro like `@exported_enum` in that
it provides more control by decoupling definition from exportation.

- Use `@exported_enum` when you always want to define and export an enum and all its members in one step.
- Use `@export_enum_members` when you have an existing enum and only want to export its members, potentially keeping the enum type itself private to the module.

# See Also
- `exported_enum`
- `@enum`: The built-in macro for creating enumerations in Julia.
- `export`: The keyword for defining a module's public API.
"""
macro export_enum_members(enum_type)
    # This macro must be called after the enum is defined.
    # We evaluate the enum_type symbol in the context of the module where
    # the macro is called (__module__) to get the actual type object.
    try
        type_obj = Core.eval(__module__, enum_type)

        # Check that we have a valid Enum
        if !(type_obj isa DataType && supertype(type_obj) <: Enum)
            return :(error($(string(enum_type)) * " is not a valid Enum type."))
        end

        # Get the list of instances (e.g., [PENCIL, BRUSH, ...])
        instance_list = instances(type_obj)

        # Create a list of export expressions, e.g., [:(export PENCIL), :(export BRUSH), ...]
        export_expressions = [:(export $(Symbol(inst))) for inst in instance_list]

        # Return the block of expressions, escaped to run in the caller's scope.
        # The `...` splats the array of expressions into the quote block.
        return esc(quote
            $(export_expressions...)
        end)
    catch e
        return :(error("Could not find enum type " * $(string(enum_type)) * ". It must be defined before calling @export_enum_members."))
    end
end

"""
    @exported_enum(name, args...)

Defines an enum and automatically exports both the enum type and all of its instances
from the current module.

This macro is a convenience wrapper around Julia's built-in `@enum` macro. It is designed
to reduce boilerplate code by eliminating the need to manually write `export` statements
for the enum type and each of its members.

# Usage

The macro is called with the same syntax as `@enum`, typically using a `begin ... end`
block.

```julia
@exported_enum MyEnum begin
    ValueA = 1
    ValueB = 2
    ValueC
end
```

# Examples

Consider a module that needs to provide an enum as part of its public API.

#### Without the macro:

Without this macro, you must define the enum and then manually list the type and every single member in an `export` statement.

```julia
module MyModule

@enum MyEnum begin
    ValueA = 1
    ValueB = 2
    ValueC
end

# Manual export statements are required, which can be verbose.
export MyEnum, ValueA, ValueB, ValueC

end
```

#### With the `@exported_enum` macro:

The macro handles the definition and all exports in a single, clean expression, making your code more concise and less error-prone.

```julia
module MyModule

@exported_enum MyEnum begin
    ValueA = 1
    ValueB = 2
    ValueC
end

# No further export statements are needed for the enum.

end
```

The macro call above automatically expands to the following code during compilation:
```julia
@enum MyEnum begin
    ValueA = 1
    ValueB = 2
    ValueC
end
export MyEnum
export ValueA
export ValueB
export ValueC
```

# See Also
- `export_enum_members`
- `@enum`: The built-in macro for creating enumerations in Julia.
- `export`: The keyword for defining a module's public API.
"""
macro exported_enum(name, args...)
    # Helper function to extract the symbol name from an enum member definition.
    # This correctly handles both `ValueA` and `ValueA = 1`.
    function get_enum_symbol(arg)
        if arg isa Symbol
            return arg
        elseif arg isa Expr && arg.head == :(=) && arg.args[1] isa Symbol
            return arg.args[1]
        else
            return nothing # Ignore things like LineNumberNodes or comments
        end
    end

    local enum_definition_args # The raw arguments for the @enum macro
    local member_names         # The clean symbols for the export list

    if length(args) == 1 && args[1] isa Expr && args[1].head == :block
        # Handle the `begin ... end` block syntax
        block = args[1]
        enum_definition_args = block
        member_names = filter(!isnothing, [get_enum_symbol(arg) for arg in block.args])
    else
        # Handle the standard `@enum(name, ValueA, ValueB=2)` syntax
        enum_definition_args = args
        member_names = filter(!isnothing, [get_enum_symbol(arg) for arg in enum_definition_args])
    end

    # The @enum macro can take the block or the splatted args directly.
    # We construct the appropriate call based on the syntax used.
    enum_definition = if enum_definition_args isa Expr && enum_definition_args.head == :block
        :(@enum $name $enum_definition_args)
    else
        :(@enum $name $(enum_definition_args...))
    end

    # We escape the final generated code block to ensure it is executed
    # in the scope where the macro was called.
    return esc(quote
        $enum_definition
        export $name
        $([:(export $name) for name in member_names]...)
    end)
end