Automatically generating boilerplate code

I have the following repetitive code structure.

mutable struct SC011
    state::SCBit
    info::String
    nin::Int
    nout::Int
    SC011() = new(false, "(1-λ₁)/(1-λ₁λ₂)", 2, 1)
end

Base.broadcastable(g::SC011) = Ref(g)

function proc(g::SC011, b1::SCBit, b2::SCBit)
    if !(ismissing(b1) || ismissing(b2))
        g.state = !b1 | g.state & b2
        return g.state
    end
    handle_missing_two_inputs("SC011", b1, b2)
end

… plus a few other methods defined for SC011.

I have dozens of these to implement, where the only change from one type to the next is the type name SC011, the string "(1-λ₁)/(1-λ₁λ₂)", and the expression !b1 | g.state & b2.

It would obviously be great to automate all this. I was hoping @generated would be the key, but it seems macros can’t be used to define a new type. Maybe I just don’t know what I’m doing. I remember seeing a blog post where someone automated a lot of boilerplate code using reasonably simple metaprogramming, but I can’t find it now and I’m not sure if it’s relevant here.

Is automating this possible, and if so can anyone provide a pointer to a similar example please? Many thanks.

You can loop over (name,string,expr) and use @eval to generate the code

for (name,string,expr)∈((:SC011,"(1-λ₁)/(1-λ₁λ₂)",:(!b1 | g.state & b2)),)
	@eval begin
		mutable struct $name
			state::SCBit
			info::String
			nin::Int
			nout::Int
			$name() = new(false, $string, 2, 1)
		end

		Base.broadcastable(g::$name) = Ref(g)

		function proc(g::$name, b1::SCBit, b2::SCBit)
			if !(ismissing(b1) || ismissing(b2))
				g.state = $expr
				return g.state
			end
			handle_missing_two_inputs("$name", b1, b2)
		end
	end
end
5 Likes

Wow, the people on this site are amazing!

3 Likes