March 08, 2003
>>My goal is merely to cite that graphs can be
>>done with stock C++ template programming.
>
>How?  Show me.


Read the docs and keep the pizza.  The subject is moot if graphs are not the topic.



>>http://www.digitalmars.com/drn-bin/wwwnews?D/11125
>I sometimes go back to that [strategic programming] link when I need a good laugh.

Uh huh.  You should be getting more than a laugh out of them because......

>A meta-program can generate any code.  It's only limited
>by it's input, which is the user's program.

...you have described program transformation, which is, in their own words....

..."The prime application domain for strategic programming."

http://www.program-transformation.org/twiki/bin/view/Transform/GenerativeProgramming "The goal of generative programming is to replace manual search, adaptation, and assembly of components with the automatic generation of needed components on demand."


> I don't know of any common term that applies.
> If there is one, I'd like to know what it is.

Try 'automatic code generation' or 'generative programming.' The term 'meta-programming' connotes templates qua templates.


>Try writing a template that writes out your classes and
>their members and methods in formatted HTML with hyper-links and all.
>It's easy in a meta-program.  How about a template that performs a full >database check for dangling pointers, or automatic binary or text based
>load/save?  DataDraw does this today.

From those hilarious clowns at the strategic programming circus,

http://www.program-transformation.org/twiki/bin/view/Transform/ProgramTransformation "Program transformation is used in many areas of software engineering, including compiler construction, software visualization, documentation generation, and automatic software renovation."


>>'type inference' ... is exactly what the Functional C++ library does.
>I don't see what functional programming has to do with this.

Type inference was the point, not functional programming.  You were yakking about extending types, as far as I could tell.


>>When you say
>>'Templates can't add members to multiple existing classes'
>>do you mean 'aspect-oriented programming' or 'multimethods'
>>or 'generic methods'?
>Definately not any of those.  I mean that templates don't modify existing classes at all.  Meta programs can.

I'm not 'definitely' sure of anything you're saying.  Your whole problem may be ill-posed in which case you could have us all chasing our tails.  If you really need to 'add members to multiple existing classes' then first-class multimethods are your only hope this side of a dynamically typed and introspective language or automatic code generation.  I think you should start asking hard questions about whether class-based encapsultaion is the proper way to structure your code, since it is getting in your way.

Mark


March 08, 2003
Toyotomi <io219@attbi.com> writes:
> I still don't get it. You can pass function pointers, you can design with interfaces, or you can use variants, all depending what language you're in of course... Templates seem to allow the same, except they require new syntax and terminology, both of which are very confusing in C++ for me.
>
> eg. How are templates different than an OO implementation with interfaces?

Templates are for compile-time polymorphism, and object-orientation and interfaces/abstract classes are for run-time polymorphism.

You can parameterize any type with templates, including built-in types, which you cannot use with abstract classes without boxing. With templates you get generally less overhead.  Templates are more costly to compile, though, since you usually need more access to the source code.  Typically templates are included as header files, while you can compile classes separately.

In general, templates do the same thing at compile time as abstract classes do at run time.  This means that you have complete access to the type you're using.  You know its size, meaning that you can store it on the stack, embed it in objects, and copy it as you like.  If you access an object only through its interface, you only know the operations it supports and someone has to allocate it on the heap.

So, both have their uses.  However, lately the trend in the mainstream imperative languages seems to have been towards the object-oriented paradigm (Java and C# as major examples).

A short introduction to C++ templates can be found at:

http://www.codeguru.com/atl/KD062002.html

Generic programming in C++ is a form of object-oriented programming where the "object-oriented part", the type selection, is done at compile time.  "Interfaces" are replaced with the notion of "concepts", which is an abstract concept not specifically supported by the language.  (In this sense, you could it a programming discipline.) An introduction to generic programming (that might not be the most approachable, though) can be found at

http://www.sgi.com/tech/stl/stl_introduction.html

As to why C++ is not just an object-programming language, please see "Why C++ is not just an object-oriented programming language":

http://www.research.att.com/resources/articles/oopsla.pdf

I also heartily recommend the book "The C++ Programming Language" which answers these questions and many more.

Oh, and if you have time to visit your local bookstore and take a look at the chapter 6 of the book "Generative Programming" by Czarnecki and Eisenecker, do that.  It provides a thorough comparison between parametric (compile time/templates) and subtype (run time/interfaces) polymorphism.

And check out the basics (or at least the introduction) from "C++ Templates - the Complete Guide" by Vandevoorde and Josuttis, while you're at it.  Happy learning :)

-Antti

March 08, 2003
Mark Evans <Mark_member@pathlink.com> writes:

> Bill-
>
> My interest in your thoughts is frustrated because I can't follow them.  Hazarding a guess: they confound multiple issues and eschew standard terms.

If you'll permit me to present my view of what Bill is wishing for -- I remember that I had a vision of something similar earlier...  I'll forget about the standard terminology and present the hypothetical compile-time metaprogramming with a concrete example.

First, by adding a member to multiple classes I believe he means member variables, not functions.  Hence it's something that resembles more aspect-oriented programming than multimethods.  But compile-time metaprogramming could do more by transforming and modifying the syntax tree in almost any way.

Suppose we have a simple class with member "int x" and getter/setter member functions.

class X
{
    int get_x() { return x; }
    void set_x(int new_x) { x = new_x; }

private:
    int x;
}

Now we want to make a synchronized version of it.  This one requires a
mutex (reentrant, actually, but that's not the point) in the object
and lock() and release() calls before and after, respectively, the
member functions are called

class Synchronized_X
{
    int get_x() {
        mutex.lock();
        int result = x;
        mutex.release();
        return result;
    }

    void set_x(int new_x) {
        mutex.lock();
        x = new_x;
        mutex_release();
    }

private:
    Mutex mutex;
    int x;
}

A-ha, there's a "cross-cutting aspect", namely, synchronization! Synchronization is nice, but sometimes we don't want it.  In this particular case, I want to be able to toggle the synchronization of X - or actually, all objects - with one change.  Looks like a case for aspect-oriented programming.  But I never really got that AOP thing, it seems so complex and I haven't seen a really good hands-on example. I'd really just prefer to do something like this:

// A metafunction that takes a type as an argument and returns a type
// which behaves as original type but is synchronized.  I use
// <> to represent the fact that the function is called at
// compile-time, not at run-time -- it has no particular resemblance
// to C++ templates.  At least yet.  "Type type" is treated as if
// passed by value.

Type Synchronized<Type type>
{
    type.insert_private_member("Mutex mutex;");

    for_each (member_function in type.member_functions)
    {
        member_function.insert_statement_at_beginning("mutex.lock();");
        member_function.insert_statement_at_end("mutex.release();");
    }

    return type;
}

(Obviously, this version isn't perfect.  Synchronized<> would need to traverse all statements in each member function and look for return statements, and then prepend each return statement with an introduction of a temporary variable, which would be initialized with the expression in the return statement.  Also it would have to survive name clashes if the function also had a "mutex", etc.)

Now that we have Synchronized<>, we can get a synchronized database connection with:

Synchronized<Database_Connection> db;

Alternatively, we can parameterize all our classes that need to be synchronized in a manner like this:

global.d:

Type Identity<Type type> { return type; }

version (multi_threaded)
{
    alias Synchronized Synch;
}
else
{
    alias Identity Synch;
}

And then use Synch<Classname> anywhere where multithreading might potentially cause harm.

(Of course, there's "synchronized" already but let's forget that for
the sake of the example.)

The problem is that the interface to "Type" and possibly other compile-time things (functions, aliases, symbols, strings, constants, you name it) probably needs a lot of experimentation before getting it right.  The change to the language as it is now isn't exactly trivial and I don't expect to see it in D, at least in the near future. Actually, these kinds of features would almost need a language of their own.

Metafunctions should be side-effectless, so that we could be sure that Function<Type> always means the same thing.  Then the compiler also wouldn't need to evaluate it more than once per compilation.

Another question is: should metafunctions be compiled?  I was first thinking about interpreting them, but of course it would be more efficient to compile them to native code and link them into the compiler -- Bill has a point there.

In any case, this kind of metaprogramming would be cool, definitely, and you'd get templates for free if you could define local types in terms of argument types and return them.  Metaprogramming is a generalization of templates.  Hard-core C++ guys do "metaprogramming by templates" with great pain and suffering, but one could do "templates by metaprogramming" with naturalness and ease:

Type List<Type type>
{
    // a local type definition, using "type";
    struct ListNode
    {
        ListNode<type>* next;
        type item;
    }

    struct List
    {
        ListNode<type>* head;
    }

    return List;
}

f()
{
    List<int> x;
}

And there you go.  Just add member functions.

One would still need a way to manipulate functions in a similar way, to get generic functions, and automatic type inference would be a problem.

-Antti
March 08, 2003
In article <87d6l1isae.fsf@hoastest1-8c.hoasnet.inet.fi>, Antti Sykari says...
>
>Mark Evans <Mark_member@pathlink.com> writes:
>
>> Bill-
>>
>> My interest in your thoughts is frustrated because I can't follow them.  Hazarding a guess: they confound multiple issues and eschew standard terms.
>
>If you'll permit me to present my view of what Bill is wishing for -- I remember that I had a vision of something similar earlier...  I'll forget about the standard terminology and present the hypothetical compile-time metaprogramming with a concrete example.
>
>First, by adding a member to multiple classes I believe he means member variables, not functions.  Hence it's something that resembles more aspect-oriented programming than multimethods.  But compile-time metaprogramming could do more by transforming and modifying the syntax tree in almost any way.
>
>Suppose we have a simple class with member "int x" and getter/setter member functions.
>
>class X
>{
>    int get_x() { return x; }
>    void set_x(int new_x) { x = new_x; }
>
>private:
>    int x;
>}
>
>Now we want to make a synchronized version of it.  This one requires a
>mutex (reentrant, actually, but that's not the point) in the object
>and lock() and release() calls before and after, respectively, the
>member functions are called
>
>class Synchronized_X
>{
>    int get_x() {
>        mutex.lock();
>        int result = x;
>        mutex.release();
>        return result;
>    }
>
>    void set_x(int new_x) {
>        mutex.lock();
>        x = new_x;
>        mutex_release();
>    }
>
>private:
>    Mutex mutex;
>    int x;
>}
>
>A-ha, there's a "cross-cutting aspect", namely, synchronization! Synchronization is nice, but sometimes we don't want it.  In this particular case, I want to be able to toggle the synchronization of X - or actually, all objects - with one change.  Looks like a case for aspect-oriented programming.  But I never really got that AOP thing, it seems so complex and I haven't seen a really good hands-on example. I'd really just prefer to do something like this:
>
>// A metafunction that takes a type as an argument and returns a type
>// which behaves as original type but is synchronized.  I use
>// <> to represent the fact that the function is called at
>// compile-time, not at run-time -- it has no particular resemblance
>// to C++ templates.  At least yet.  "Type type" is treated as if
>// passed by value.
>
>Type Synchronized<Type type>
>{
>    type.insert_private_member("Mutex mutex;");
>
>    for_each (member_function in type.member_functions)
>    {
>        member_function.insert_statement_at_beginning("mutex.lock();");
>        member_function.insert_statement_at_end("mutex.release();");
>    }
>
>    return type;
>}
>
>(Obviously, this version isn't perfect.  Synchronized<> would need to traverse all statements in each member function and look for return statements, and then prepend each return statement with an introduction of a temporary variable, which would be initialized with the expression in the return statement.  Also it would have to survive name clashes if the function also had a "mutex", etc.)
>
>Now that we have Synchronized<>, we can get a synchronized database connection with:
>
>Synchronized<Database_Connection> db;
>
>Alternatively, we can parameterize all our classes that need to be synchronized in a manner like this:
>
>global.d:
>
>Type Identity<Type type> { return type; }
>
>version (multi_threaded)
>{
>    alias Synchronized Synch;
>}
>else
>{
>    alias Identity Synch;
>}
>
>And then use Synch<Classname> anywhere where multithreading might potentially cause harm.
>
>(Of course, there's "synchronized" already but let's forget that for
>the sake of the example.)
>
>The problem is that the interface to "Type" and possibly other compile-time things (functions, aliases, symbols, strings, constants, you name it) probably needs a lot of experimentation before getting it right.  The change to the language as it is now isn't exactly trivial and I don't expect to see it in D, at least in the near future. Actually, these kinds of features would almost need a language of their own.
>
>Metafunctions should be side-effectless, so that we could be sure that Function<Type> always means the same thing.  Then the compiler also wouldn't need to evaluate it more than once per compilation.
>
>Another question is: should metafunctions be compiled?  I was first thinking about interpreting them, but of course it would be more efficient to compile them to native code and link them into the compiler -- Bill has a point there.
>
>In any case, this kind of metaprogramming would be cool, definitely, and you'd get templates for free if you could define local types in terms of argument types and return them.  Metaprogramming is a generalization of templates.  Hard-core C++ guys do "metaprogramming by templates" with great pain and suffering, but one could do "templates by metaprogramming" with naturalness and ease:
>
>Type List<Type type>
>{
>    // a local type definition, using "type";
>    struct ListNode
>    {
>        ListNode<type>* next;
>        type item;
>    }
>
>    struct List
>    {
>        ListNode<type>* head;
>    }
>
>    return List;
>}
>
>f()
>{
>    List<int> x;
>}
>
>And there you go.  Just add member functions.
>
>One would still need a way to manipulate functions in a similar way, to get generic functions, and automatic type inference would be a problem.
>
>-Antti

I agree with all these points, including that it's not likely to make it into D. I'm not sure where to go with this idea from here, other than implementing a proto-type in my little test compiler.

Bill


1 2
Next ›   Last »