Skip to content

Implementation of fullyQualifiedTypename template that operates on raw t... #863

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 17, 2013
Merged

Implementation of fullyQualifiedTypename template that operates on raw t... #863

merged 8 commits into from
Jan 17, 2013

Conversation

mihails-strasuns
Copy link

...ypes contrary to fullyQualifiedName which is tied to aliases to symbols.

Original need in such function came from vibe.d rest module I have been working on lately to improve reliability of automatic REST client generation. I.e. module A needs to generate a class that implements methods of interface defined in module b. Method may return symbol typed with nested hierarchy and that is it.

After some tweaking template looked generic enough so that it may be a fit to phobos so here am I :)

Destroy (c)

* static assert(fullyQualifiedTypename!(const MyStruct[]) == "const(mymodule.MyStruct[])");
* ---
*/
template fullyQualifiedTypename(T)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeName, not Typename.

@alexrp
Copy link
Member

alexrp commented Oct 20, 2012

This seems OK to me, but I won't claim to be an expert on D's metaprogramming. Can anyone else chime in?

@alexrp
Copy link
Member

alexrp commented Oct 31, 2012

Need to make the auto tester run this one.

import std.conv;

enum fullyQualifiedTypeNameImpl = chain!(
fullyQualifiedTypeNameImpl!(typeof(T.init[0]), qualifiers) ~ "["~to!string(T.length)~"]"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const(int)[3] and const(int[3]) should be merged to const(int[3]), because it is compatible with compiler's output.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I double checked this and that is actually how it works now. Simple comparison chart:

const(int)[3]
typeid: const(int[3])
fullyQualifiedTypeName: const(int[3])

const(int[3])
typeid: const(int[3])
fullyQualifiedTypeName: const(int[3])

const(int[])
typeid: const(const(int)[])
fullyQualifiedTypeName: const(int[])

So actually difference is for dynamic slices and as far as I understand const it is typeid output here which is redundant.

@9rnsr
Copy link
Contributor

9rnsr commented Oct 31, 2012

Can you merge fullyQualifiedTypeName into fullyQualifiedName?

@mihails-strasuns
Copy link
Author

@9rnsr Is it possible? fullyQualifiedName accepts symbols (alias) and fullyQualifiedTypeName - plain types. What could have been common declaration for merged one?

Thanks for your extensive comments, I was sure there will be a lot of stupid mistakes. I ll adress them closer to this weekend.

@9rnsr
Copy link
Contributor

9rnsr commented Oct 31, 2012

As far as I see, fullyQualifiedTypeName just works for the built-in types (for the aggregate types, you simply forward it to fullyQualifiedName).

Next, template overloads for the alias/type parameter, it works as follows.

struct S {}
template T() {}
template Test(alias A) { enum Test = 1; }
template Test(T) { enum Test = 2; }
static assert(Test!S == 1);  // aggregate symbol matches alias parameter
static assert(Test!T == 1);  // ditto
static assert(Test!string == 2);  // built-in types matches type parameter

So, I think you can simply add an overloaded template as like:

template fullyQualifiedName(alias T) { ... }
template fullyQualifiedName(T) { .. }   // your new implementation

@mihails-strasuns
Copy link
Author

Ah, so not exactly merge into one template but overload with same name? Sure, will do.

@@ -254,11 +254,20 @@ template fullyQualifiedName(alias T)

version(unittest)
{
struct Outer
// Used for both fullyQualifiedName and fullyQualifiedTypeName unittests
struct QualifiedNameTests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make private

@mihails-strasuns
Copy link
Author

Rebased, most comments adressed.
@9rnsr Ping, could you clarify those two comments I have not understood above?


ref const(Inner[string]) func( ref Inner var1, lazy scope string var2 );
shared(const(Inner[string])[]) data;
const Inner delegate(double, string) deleg;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove trailing spaces.

@9rnsr
Copy link
Contributor

9rnsr commented Nov 13, 2012

Remove trailing redundant spaces on the whole.

@9rnsr
Copy link
Contributor

9rnsr commented Nov 13, 2012

Good work, @Dicebot ! Except function pointer type formatting, looks good to me.

@9rnsr
Copy link
Contributor

9rnsr commented Nov 13, 2012

My humble proposals:

  1. Adding three special type formatting for string<-immutable(char)[], wstring<-immutable(wchar)[], and dstring<-immutable(dchar)[].
  2. More readable unittest as like:
unittest
{
    alias fqn = fullyQualifiedName;
    enum Inn = "std.traits.QualifiedNameTests.Inner";
    with (QualifiedNameTests)
    {
        static assert(fqn!(string) == "immutable(char)[]");
        static assert(fqn!(Inner) == Inn);
        static assert(fqn!(typeof(array)) == Inn~"[]");
        static assert(fqn!(typeof(sarray)) == Inn~"[16]");
        static assert(fqn!(typeof(aarray))  == Inn~"["~Inn~"]");
        static assert(fqn!(ReturnType!func) == "const("~Inn~"[immutable(char)[]])");
        static assert(fqn!(typeof(func)) == "const("~Inn~"[immutable(char)[]])("~Inn~", immutable(char)[])");
        static assert(fqn!(typeof(data)) == "shared(const("~Inn~"[immutable(char)[]])[])");
        static assert(fqn!(typeof(deleg)) == "const("~Inn~" delegate(double, immutable(char)[]))");
        static assert(fqn!(typeof(funcPtr)) == Inn~"(double, immutable(char)[])*");
    }
}

else static if (isSomeFunction!T)
{
static if (isDelegate!T)
enum format_str = "%s delegate(%s)";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delegate and function may have some attributes. Furthermore, delegate may have qualifier as the function type.
For example:

shared(immutable(Inner) delegate(double, string) const shared nothrow) dg;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shoo I have experimented a bit with this and explored spec more and still not sure what to do about this. writeln(typeid(typeof(dg))) just ignores almost anything so is hardly a reference and I wonder if shared and nothrow are part of a type, as those are function attributes and generally belong to symbol.

Also current dmd prohibits me from declaring delegate variable of such type: "Error: const/immutable/shared/inout attributes are only valid for non-static member functions", sounds like a bug?

tl;dr: implementing is not a problem but I need someone to guide me here on theoretical language-related side.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@9rnsr @andralex probably you could also give some input regarding the point @shoo has raised?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With git head, directly declaring qualified delegate type (e.g. void delegate() const) is allowed. It is consistent with a member function void foo() const.
So, adding qualifier to delegate type is necessary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And, TypeInfo.toString() sometimes returns a bad representation of the type. The lack of qualifier for delegate types is one of them.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, it seems that type qualifiers and storage classes / function attributes are frequently mixed in dlang.org docs.
Am I understanding it right that in @shoo example const/shared are type qualifiers and should be stringified and nothrow is storage class? Using http://dlang.org/declaration.html as a reference currently.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using http://dlang.org/declaration.html as a reference currently.

Oh, I found a documentation bug in website.

BasicType2:
  ...
  delegate Parameters FunctionAttributesopt

FunctionAttributes:
  FunctionAttribute
  FunctionAttribute FunctionAttributes

FunctionAttribute:
  nothrow
  pure
  Property

Property:
  @ PropertyIdentifier

In current spec, delegate type declaration cannot have postfix qualifiers.
I think BasicType2 should be:

BasicType2:
  ...
  delegate Parameters MemberFunctionAttributeopt

MemberFunctionAttributes:
  MemberFunctionAttribute
  MemberFunctionAttribute MemberFunctionAttributes

MemberFunctionAttribute:
  const
  immutable
  inout
  shared
  FunctionAttribute

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, posted pull request.
dlang/dlang.org#218

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Delegate type qualifier cannot test directly. You should extract function type from it, then test const.

void main() {
  alias void delegate() const DG;
  alias const(void delegate() const) CDG;
  static assert(!is( DG == const));
  static assert( is(CDG == const));

  static if (is(DG F == delegate))  // extract function type from DG
  {
    // F is a "const function type"
    pragma(msg, F);  // const void()
    static assert(is(F == const));  // pass
  }
}

@mihails-strasuns
Copy link
Author

Sorry guys, i am currently in a hospital after apoplexy, forced to pause this. Will response as soon as be back on my feet.

@shoo
Copy link
Collaborator

shoo commented Nov 24, 2012

Take care of yourself.

@mihails-strasuns
Copy link
Author

Rebased, squashed, implemented @9rnsr proposals.
Need to experiment a bit more regarding attributes and qualifiers for function types.

static assert(fqn!(typeof(func)) == xformat("const(%s[string])(%s, string)", inner_name, inner_name));
static assert(fqn!(typeof(data)) == xformat("shared(const(%s[string])[])", inner_name));
static assert(fqn!(typeof(deleg)) == xformat("const(%s delegate(double, string))", inner_name));
static assert(fqn!(typeof(funcPtr)) == xformat("%s function(double, string)", inner_name));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two weeks ago, the implementation of std.string.format is changed to xformat's. (by merging #939). So currently, you can just use format in here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally! :)

@mihails-strasuns
Copy link
Author

I am recovering more slowly than I hoped and that took some time but feels like all "interesting" cases for function type stringification were addressed. May be missed some corner cases, unit test proposals are welcome. But so far it is done, awaiting for next comment portion :)

@mihails-strasuns
Copy link
Author

Noticed test suite issues, made a quick fix. I am unsure regarding @Property applicability here though - those utility functions do not really look like ones, but stuff staticMap assumes its first parameter is either plain template or @Property function. Are those fine or shall I change staticMap to accept template functions with zero arguments?
@9rnsr @andralex

@9rnsr
Copy link
Contributor

9rnsr commented Jan 3, 2013

Excepting just one comment, looks good to me. Nice job, @Dicebot !

Noticed test suite issues, made a quick fix.

It is an enhancement for staticMap. AFAIK, historically staticMas didn't consider to be given a CTFE-able function as a predicate. In recent, by the improvement of CTFE and @property enforcement, staticMap could accept a CTFE-able property function as a predicate syntactically. But there is yet not consideration for non-@property function.

@mihails-strasuns
Copy link
Author

It is done, but blocked by of ParameterStorageClassTuple, which seems unaware of inout. Made relevant comment here:
http://d.puremagic.com/issues/show_bug.cgi?id=8695
Gonna have a look at it.

@mihails-strasuns
Copy link
Author

Dicebot added 6 commits January 16, 2013 13:41
…w types contrary to fullyQualifiedName which is tied to aliases to symbols
1) Added formatting for:
    * Attributes
    * Parameter storage classes
    * Variadic arguments
    * Function type qualifiers for delegates
    * Function linkage
2) Unit tests were re-arranged and improved a bit
3) xformat -> format
…t stack anymore, turned one of inner templates to CTFE function
@mihails-strasuns
Copy link
Author

@9rnsr Thanks for fast fix. Another problem: I have found that any addition of qualifiers break compatibility between two fullyQualifiedName templates for user-defined symbols. Consider this:

import std.stdio;

struct Tmp 
{
}

template oops(alias T)
{
    pragma(msg, "alias");
    enum oops  = T.stringof;
}

template oops(T)
{
    pragma(msg, "type");
    enum oops = T.stringof;
}

void main()
{
    alias Type = const(Tmp);
    writeln(oops!Type);
}

I can't imagine any workaround for this now. Unit test is failing currently because of this problem, I pushed last changes anyway in case any additional comments may arise.

@9rnsr
Copy link
Contributor

9rnsr commented Jan 16, 2013

You can make one entry template with tuple template parameter.

struct Tmp {}

template oops(T...)
{
    pragma(msg, "tuple");
    enum oops  = T[0].stringof;
    pragma(msg, isAggregateType!(T[0]));  // T[0] is user defined type(symbol) or not
}

void main()
{
    alias Type = const(Tmp);
    pragma(msg, oops!Type);
}

@mihails-strasuns
Copy link
Author

@9rnsr Thanks. Your sample was not generic enough but provided me with insight of more elegant solution via is(T) :)
Should work now for all discussed cases.
Anything else?

import std.conv;

enum fullyQualifiedNameImplForTypes = chain!(
format("%s[%s]", fullyQualifiedNameImplForTypes!(typeof(T.init[0]), qualifiers), to!string(T.length))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling to!string is not need anymore.

@9rnsr
Copy link
Contributor

9rnsr commented Jan 17, 2013

Reviewed and commented.
Code has been improved steadily. :)

@mihails-strasuns
Copy link
Author

All comments have been addressed.
Anything else? :)

@9rnsr
Copy link
Contributor

9rnsr commented Jan 17, 2013

Looks good to me! Let's wait the auto-tester result.

@9rnsr
Copy link
Contributor

9rnsr commented Jan 17, 2013

OK, now the auto-tester result is all green.

9rnsr added a commit that referenced this pull request Jan 17, 2013
Implementation of fullyQualifiedTypename template that operates on raw t...
@9rnsr 9rnsr merged commit 86809da into dlang:master Jan 17, 2013
@9rnsr
Copy link
Contributor

9rnsr commented Jan 17, 2013

Merged. Thanks for your long work, @Dicebot !

@mihails-strasuns mihails-strasuns deleted the fully_qualif_typename branch January 17, 2013 14:50
else static if (variadic == Variadic.typesafe)
enum variadicStr = " ...";
else
static assert(0, "New variadic style has been added, please update fullyQualifiedName implementation");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not simply use a final switch here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An artifact of old template-only non-CTFE approach that did not catch my attention in time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants