You won’t need to directly use Markdown.parse(docstr), since that’s what
julia does for you anyway, see here
<https://github.com/JuliaLang/julia/blob/baf174f5c252a438006c6f23ed7097fbab004025/base/docs/Docs.jl#L437-L443>,
where any plain string or interpolated string expression gets wrapped in a
markdown parsing call automatically when passed to @doc.
Also, note that defining new variables inside the returned block, in this
case dot_op and docstr, will be visible (though gensym’d) from the calling
env, ie.
macro foo_op(op)
quote
dot_op = symbol(".$($op)")
docstr = "`$($op)` also works for `Foo` types!"
function Base.$op(a::Foo, b::Foo)
Foo(dot_op(a.x, b.x))
end
@doc Markdown.parse(docstr) Base.$op
Base.$op
end
end
julia> @foo_op +
julia> names(Main)
10-element Array{Symbol,1}:
symbol("#3#dot_op")
symbol("#4#docstr")
symbol("@foo_op")
:Base
:Core
:Foo
:Main
:User
:__META__
:ans
Notice symbol("#3#dot_op") and symbol("#4#docstr"). Each time you call the
macro you’ll generate a new pair. They won’t clash with any other
variables, but it’s worth trying to avoid creating them to keep the
namespace clean. Both the variables aren’t really needed anyway since docstr's
value could just be placed directly into the @doc.
The other variable, dot_op, might actually cause some trouble
(performance-wise) since you’re defining a non-const global that aliases a
function and then calling it from inside a function. Compare the output of
the @code_* macros for your macro and the following
version that doesn’t define temporary variables:
julia> macro foo_op_direct(op)
quote
function Base.$op(a::Foo, b::Foo)
Foo($(symbol(".", op))(a.x, b.x))
end
@doc "`$($op)` also works for `Foo` types!" Base.$op
Base.$op
end
end
julia> type Foo
x :: Int
end
julia> @foo_op_direct +
+ (generic function with 166 methods)
julia> code_llvm(+, (Foo, Foo))
...
The code generated by @foo_op_direct will be quite a bit shorter than that
of @foo_op.
— Mike
On Thursday, 17 December 2015 07:16:09 UTC+2, Ismael Venegas Castelló wrote:
>
> I didn't know of "$($x)" to interpolate inside stirng inside expresion,
> thanks!
>
> This also works:
>
> macro foo_op(op)
> quote
> dot_op = symbol(".$($op)")
> docstr = "`$($op)` also works for `Foo` types!"
> function Base.$op(a::Foo, b::Foo)
> Foo(dot_op(a.x, b.x))
> end
> @doc Markdown.parse(docstr) Base.$op
> Base.$op
> end
> end
>
> El miércoles, 16 de diciembre de 2015, 11:19:24 (UTC-6), Michael Hatherly
> escribió:
>>
>> If the macro returns a single documentable expression, such as a function
>> or type, then you shouldn’t need to do anything special, just add an @doc
>> to the for-loop that generates the methods.
>>
>> If the macro returns a block expression then you need to tell the
>> docsystem how you’d like to handle it. Use Base.@__doc__ to mark the
>> subexpression you’d like to document in expression returned by the macro.
>> See this section of the manual,
>> http://docs.julialang.org/en/latest/manual/documentation/#macro-generated-code,
>>
>> for an example. Something like the following should work for the code in
>> this thread:
>>
>> macro operator_obs(name)
>> M = esc(:M)
>> n = esc(name)
>> d = esc(symbol(".", name))
>> quote
>> import Base: $n
>> Base.@__doc__ $(n)(m_1::$M, m_2::$M) = $(M)($(d)(m_1.a, m_2.a),
>> $(d)(m_1.b, m_2.b))
>> end
>> end
>>
>> for op in [:+, :-, :*, :/]
>> @eval begin
>> @doc "`$($op)` docs for type `M`." ->
>> @operator_obs $op
>> end
>> end
>>
>> help?> M(1, 2) + M(2, 3)
>> + docs for type M.
>>
>> help?> M(1, 2) / M(2, 3)
>> / docs for type M.
>>
>> Documenting code generated by a function, rather than a macro, isn’t
>> going to be as clean though. You’d need to use @doc after defining the
>> code, perhaps something like:
>>
>> for op in [:+, :-, :*, :/]
>> @eval begin
>> operator_obs($op)
>> @doc "`$($op)` docs for type `M`." $(op)(::M, ::M)
>> end
>> end
>>
>> to add docs to the correct op method.
>>
>> — Mike
>>
>>
>>
>> On Wednesday, 16 December 2015 17:41:36 UTC+2, j verzani wrote:
>>>
>>> While this topic is active, can anyone suggest how this can be
>>> incorporated with docstrings to add help entries to the newly generated
>>> methods?
>>>
>>> On Wednesday, December 16, 2015 at 5:22:24 AM UTC-5, Greg Plowman wrote:
>>>>
>>>> I have exactly the same requirement.
>>>> Additionally, I often have more than 2 fields and also change the
>>>> fields of my custom types.
>>>>
>>>> So I use a slightly more general version of the above:
>>>>
>>>>
>>>> function CompositeBinaryOp(T::Symbol, op::Symbol)
>>>> expressions = [ :($op(x1.$field, x2.$field)) for field in
>>>> fieldnames(eval(T)) ]
>>>> body = Expr(:call, T, expressions...)
>>>> quote
>>>> function $op(x1::$T, x2::$T)
>>>> return $body
>>>> end
>>>> end
>>>> end
>>>>
>>>> type M
>>>> a
>>>> b
>>>> end
>>>>
>>>> import Base: +, -, *, /, ^
>>>>
>>>> for T in [:M]
>>>> for op in [:+, :-, :*, :/, :^]
>>>> #eval(CompositeBinaryOp(T, op))
>>>>
>>>> code = CompositeBinaryOp(T, op)
>>>> println(code, "\n")
>>>> eval(code)
>>>> end
>>>> end
>>>>
>>>>
>>>> The advantage here for me is that I can change the fields (number, type
>>>> order etc.) and operators don't need manual updating.
>>>>
>>>> I have similar functions for copy constructors, unary operators etc.
>>>>
>>>