Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Pluggable formatting

Moderators: kris

Posted: 08/13/08 19:29:08

Layout!(T) works, but the only way that I can add support for my own types is if I override toString. This doesn't allow me to specify formatting options, which is a minor annoyance sometimes. More importantly, it forces me to put presentation logic into my domain classes. That is evil.

I'd like to see a formatter system more like:

class Formatter
{
   public IFormattingProvider[TypeInfo] formatters;
   
   ...
   char[] formatSingle(TypeInfo info, void* object, char[] arguments)
   {
      if (info in formatters)
      {
         return formatters[info].format(info, object, arguments);
      }
      return defaultFormatter.format(info, object, arguments);
   }
}

The major problems I see with this setup are: - Lots of heap activity. - It doesn't deal with inheritance. If I don't have a formatter set up for a class, I probably want to default to the formatter for its parent class.

The latter can be dealt with by making formatting even more expensive, or simply not dealt with -- if I didn't provide a formatting provider for class Inherited, there's no indication that the provider for class Base will be appropriate.

I'll implement something like this and submit it, but I'm curious: how many people here would find that useful?

Author Message

Posted: 08/14/08 00:02:06

we had considerable discussions with Walter over this one and considered adding a toString(char[] format) style method to the root Object class. Problem is that we needed compiler support for structs to do something similar, and those discussions didn't really lead anywhere in particular.

The hashmap approach is something we'd considered too, but the overhead is not insignificant. Still, it is a viable approach IMO. Another approach is to explicitly convert the custom-type to a string, using whatever mechanism you deem appropriate, before injecting it into formatted output -- not ideal, but practical in the interim?

Posted: 08/14/08 01:50:28

Converting beforehand works. It involves code duplication, though, and code duplication makes me angry. The single-responsibility principle is easy to keep track of, but code duplication is not.

Speaking of code duplication, there's a lot of code in Layout that I don't want to duplicate. If Layout.munge were protected rather than private, my work would be:

class PluggableLayout?(T) : Layout!(T) {

public IFormattingProvider[TypeInfo?] providers; override T[] munge(T[] result, T[] format, TypeInfo? type, Arg p) {

if (type in providers) {

return providers[type].format(result, format, type, p);

} return super.munge(result, format, type, p);

}

}

Though I'd probably make the simplifying assumption that every IFormattingProvider deals with objects.

Posted: 08/14/08 19:47:44

dhasenan wrote:

If Layout.munge were protected rather than private, my work would be: .....

I came across this problem multiple times. I can not understand why Tango classes use private instead of protected. private is preventing anyone from extending the Tango classes and suiting them to their own needs. To do so one needs to modify the Tango sources which is not an option if you plan to update Tango often. I had those problems with Phobos too by the way.

Posted: 08/14/08 21:59:41

I also created a ticket for something similar: #1083

Posted: 08/16/08 03:16:00

Some methods are private for two reasons: the codegen is improved and, more importantly, we prefer to open things up incrementally, over time, as people request it

Posted: 08/31/08 13:33:52

I grant that it's easier to start with things closed and then open them as necessary. People complain about things being closed, but if something's unnecessarily open, you can't tell. And if someone isn't releasing their code, it probably won't be that much work to maintain the necessary changes.

For this specific problem, if Layout had an associative array of formatters in addition to its current setup, for almost everyone that array would be empty, which leads to almost no overhead. If you do use that feature, it'll slow you down, but probably not by a whole lot.

Posted: 09/01/08 21:18:17

would be great if Layout were trivially extensible with user-defined types. Not yet clear how to do that in a low-impact manner, but an AA is certainly one approach. Other ideas?

Posted: 09/20/08 16:35:26

Well, if Layout.munge were protected (and not final), then you could let other people subclass it and come up with a good solution. Moreover, you could let them come up with solutions that suit their particular needs.

Maybe I only need to override formatting for objects of one particular set of classes, and I've got a single formatting class that handles that. I could just override Layout.munge and insert a check for those classes. I wouldn't need to add that formatting class for each type it can handle; it would tell me, given a TypeInfo? object, whether it can handle it.

Maybe I have a class with a lot of subclasses that share a formatter. Then I could use builtin casts to determine whether the object should be formatted by my formatter rather than having to register for each subclass, which is error prone.

Maybe I want to leverage the layout aspects of Layout but not the formatting aspects. If I try formatting something and I don't have a custom formatter for that type, it should be an error.

Maybe I just need to add a global formatting option.

All these are possible by changing one line of code, and much more. I can't predict whether any of these will be used. But it reduces your development costs and increases the utility of Layout at the same time.

Posted: 10/05/08 04:35:30 -- Modified: 10/05/08 04:55:06 by
dhasenan -- Modified 2 Times

I notice that the ticket I opened for this was resolved as WONTFIX.

There are three issues here:

  • Arguments for formatting UDTs
  • Separation of formatting logic from the rest of a class
  • Providing formatting for library types

For example, Tango's DateTime? doesn't include a toString method. Compare that to the .NET DateTime?, which has about five different stringification options plus one that takes a format string. They're structs, so they'll get caught with Layout.unknown, but if they were classes, I'd be SOL with Layout.

IT'S ONE WORD.

Now I have to write my own formatter.

Posted: 10/05/08 05:24:59

I found a solution.

cat /usr/local/include/d/tango/text/convert/Layout.d | 
   sed 's/tango.text.convert.Layout/layout' | 
   sed 's/private/protected/' > layout.d

Posted: 10/05/08 06:54:52

There is a date/time formatter in Tango - it's in tango.locale, deliberately, such that it takes locale-specific information into account. See the relevant ticket for further info.

Perhaps it didn't occur that aggregate-type formatting could be discretely intercepted instead? That would be vastly superior to exposing the lower-level functionality of Layout, as noted in the ticket mentioned above.