From: Mathias Gaunard on
On Jul 8, 4:37 am, George Neuner <gneun...(a)comcast.net> wrote:

> I think Andre is complaining about the lack of introspection.

Runtime introspection is not particularly needed for anything real,
and certainly not functional programming.


> The point of a function object (or any closure-like device) is to use
> it generically.

I'm not sure I see what this is supposed to mean.
The point of a closure-like device is to be able to call it, combine
it with other closures, and pass it around to code that has no
knowledge of what the closure is or does, except that it accepts the
types it gives.


> C++'s lack of <dynamic typing> prevents easy use of <things you can do when you have dynamic typing>.
(Edited by myself)

Unrelated.
The fact Lisp is dynamically typed doesn't mean that this is what
functional programming is about.


> Emulating multiple dispatch with argument type checking gets messy
> very quickly.

I didn't claim it wasn't.
The best way to do multiple dispatch is certainly to use dynamic
typing. CLOS does it very nicely.


> std::function defeats the purpose entirely - it removes the ability of
> the compiler to type check arguments even for a normal call.

It does type checking all right. Trying to assign a function object
that cannot be called with the signature given will lead to a compile-
time error.


> Pattern matching is not a "run time" or "compile time" thing

I am talking about the programming feature as presented in a language
like OCaml, not the theoretical foundations behind it or whatever you
were understanding.

It is indeed a runtime thing, as a variable may be of a different type
depending on runtime conditions. The identification of what type is
being held and the appropriate dispatch to a piece of code is done at
runtime, and this is what pattern matching does.


> How exactly do you think a C++ compiler determines which overload of a
> function to call? Hint, it's described in �13.3.1 on page 292 of the
> current draft (n3090).

It happens at compile-time, yes, and I never claimed otherwise.


> The problem is that C++ overload resolution only matches static types
> at compile time, so there is no _good_ way to perform multiple
> dispatch on runtime object types.

You don't seem to understand what I was talking about, so here is an
example.

Say you have, in ML
type btree = Leaf | Node of int * btree * btree

with variant, you express that as (it's been a while since I've used
recursive ones, but I think this syntax is correct)
struct leaf_t {};
typedef tuple<int, recursive_wrapper<_>, recursive_wrapper<_>> node_t;
typedef variant<leaf_t, node_t> btree_t;

In ML, to visit it, you write

match mytree with
Leaf -> do_something_for_leaf ()
| Node(v, lft, rgt) -> do_something_for_node v lgt rgt
;

With variant, you could write

apply_visitor(make_overload<void>(
[&](leaf_t) { do_something_for_leaf(); },
[&](node_t& n) { do_something_for_node(n.get<0>(), n.get<1>(),
n.get<2>()); }
), mytree);

(Not this also works to visit multiple objects at the same time)

apply_visitor, from boost.variant, does internally the dispatch, then
casts the variant objects back to their real types, and finally calls
a polymorphic function object.
By generating that polymorphic function object through overloading
several monomorphic functions, you obtain syntax similar to that of
pattern matching.
The limitation of that technique is that you can't ask to match on
things like Node(_, Leaf, Leaf), but that kind of thing would still be
possible to do using a DSEL.



> Which is a long-winded way to accomplish 1/3 of runtime multiple
> dispatch ... namely it solves identification of the argument's runtime
> type. You still need a way to describe function argument lists and to
> match the list of variant arguments to the descriptions.

And overloading does just that; nothing prevents you however from
coding your own resolution scheme as a pure library implementation,
using a DSEL to describe what you want to match on.

(I really feel like I am repeating myself)



> Sorry, but you are misinformed. ABIs are register level function
> linkage specifications

The register-level part, that is architecture-specific, is not the
problem. This is taken care of by the C ABI anyway.


> >All platforms follow the Itanium C++ ABI adapted to their architecture,
>
> That statement doesn't even make sense. You can be certain that the
> ABIs for PowerPC, ARM, SPARC, etc. most definitely are NOT adapted
> from the Itanium.

It's called the Itanium C++ ABI, but it aims at being generic and has
very few Itanium-specific parts, easily adaptable to any other
architecture.
See <http://www.codesourcery.com/public/cxx-abi/abi.html>


> If there is a common ABI for a platform, it should be supported by all
> compilers for all languages.

Language features dictate the ABI. Consider name mangling, exceptions
and vtables.
Those are actually the things that vendors historically had difficulty
to agree on, and that is what the Itanium C++ ABI specifies.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Bo Persson on
Andre Kaufmann wrote:
> Edward Rosten wrote:
>> On Jul 4, 9:51 pm, Andre Kaufmann <akfmn...(a)t-online.de> wrote:
>>
>>>> It's an entirely different model.
>>> Many languages offer that. E.g. C#.
>>
>> That would be the .net platform rather than C# per-se. But, not
>> one of those languages/platforms has anything like the portability
>> of C++. Consider trying to make it work on a microcontroller with
>> executable code stored in NOR flash...
>
> Well, there's the .NET Micro Framework for microcontrollers.
> But anyways I don't think C++ is commonly used for microcontroller
> coder, IMHO that's commonly C code.
>
>
> Regarding portability, I rather would say, that standard C++ isn't
> that portable too. Have a look at boost, how many #idefs there are
> to ensure that it compiles under every compiler and platforms.
> And even if the C++ code is portable, you have always parts which
> call directly into OS functions (e.g. GUI), which must be
> implemented for each platform differently.
>

That is not a C++ problem, but a problem with the Boost community
trying to support 10+ years old pre-standard compilers. They do that
partly because they can. It's fun to find clever workarounds!

I have previously asked them to support only perhaps 1-2 generations
of each compiler, instead of 5-6. That way some of the libraries could
actually have more C++ code than workaround macros.


Bo Persson


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Mathias Gaunard on
On Jul 8, 4:56 am, Andre Kaufmann <akfmn...(a)t-online.de> wrote:

> I think it's rather comparable to template specialization (at least in F#)
>
> Sample: F# pattern matching
>
> let f x = match x with
> | int -> "integer"
> | _ -> "other"
>
> let value = f 8
> printfn "%A" value
>
> Prints: "integer"
>
> The compiled code results to the following pseudo code:
>
> value = "integer";
> print(value)
>
> Definitively (in this case) it's statically matched.

I don't know F# and it's too complicated to install on my OS, and
OCaml doesn't allow that exact syntax.
Since it allows to use it on any type without having to tag them,
unlike OCaml, I suppose it is quite similar to overloading. I suspect
all types are boxed with a type identifier.

But it's definitely possibly a dynamic dispatch since you couldn't
support recursive types otherwise (otherwise you'd loop). Here in this
case the compiler is being smart and optimizing it, and the fact it
does so reliably is probably support for providing overloading-like
features.


> Another sample: F#
>
> let Compose f1 f2 =
> let ret = fun n -> f1(f2 n)
> ret
>
> let Double x = x * x
> let Tripple x = x * x * x
>
> let DoubleTripple x = Compose Double Tripple x
> let result = DoubleTripple 3
> printfn "%A" result
>
> Function DoubleTripple parameter is not typed:
> I can call DoubleTripple with an integer or with an double.
>
> The best equivalent in Cpp I could get compiled is:
>
> template <typename T, typename f1, typename f2> std::function<T(T)>
> Compose(f1 foo1, f2 foo2)
> {
> return [&] (T n) { return foo1(foo2(n)); };
>
> }
>
> void foo()
> {
> auto Double = [] (int x) { return x * x; };
> auto Tripple = [] (int x) { return x * x * x; };
> auto DoubleTripple = Compose<int>(Double, Tripple);
> printf("%d\r\n", DoubleTripple(3));
>
> }
>
> How do I get rid of the type [int] ?

By not using lambdas, which are monomorphic only and kinda suck.

template<typename F1, typename F2>
struct composer
{
composer(F1&& f1_, F2&& f2_) : f1(f1_), f2(f2_) {}

template<typename... T>
auto operator()(T&&... args) -> decltype(f1(f2(args...)))
{
return f1(f2(args...));
}

private:
F1&& f1;
F2&& f2;
};

template<typename F1, typename F2>
composer<F1, F2> Compose(F1&& f1, F2&& f2)
{
return composer<F1, F2>(f1, f2);
}

This is quite more verbose, but you now have a Compose function, which
is a basic building block you should only need to write once, that
works whatever the number of arguments of f2 is, unlike in your
example.
In F# I don't think it was needed due to curryfication. Note you can
curryfy in C++ as well with a DSEL like Boost.Phoenix, but then you
have to write all your functions with it.

(Note I didn't even try to compile this because I don't have a good
compiler handy atm)

>
> >> let r f(x, y, z) = fx + fy + fz;
>
> > I do not know what this syntax is supposed to do.
>
> Should do the same as the Cpp sample ;-)

I'm pretty sure it would complain that fx, fy and fz are undefined.
As you said it was a typo.


> Is it really the case that all platforms (ARM , Apple etc.) adapted the
> Itanium C++ ABI ?

It's more of a high-level API for exceptions etc.
All major compilers appear to follow it.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Mathias Gaunard on
On Jul 8, 2:16 pm, Walter Bright <newshou...(a)digitalmars.com> wrote:

> Usually, yes, a breakpoint is better. But consider what a breakpoint is
> - it's a debugger installing an exception handler!

But not C++-like exceptions, that call destructors during stack
unwinding.
It's more like Unix signals. Or Windows structured exceptions.


> A debugger is
> sometimes not available, and so it's nice to be able to build in a bit
> of debugger capability into the program.

Installing a handler for SIGABRT seems easier than extending the
language with "special" exceptions.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Mathias Gaunard on
On Jul 6, 1:48 pm, "joe" <jc1...(a)att.net> wrote:
> Andre Kaufmann wrote:
>
> > Agreed, C# and Java strings are not perfect, but better than C++
> > standard strings regarding Unicode. I think C# has been influenced by
> > Windows Unicode support.
>
> What is wrong with fixed-width Unicode (UCS-2 or UCS-4)?

UCS-2 doesn't allow to represent all Unicode characters.
UCS-4 is a waste of memory.

And more abstract characters, like graphemes, are not fixed-width
anyway (think combining character sequences).


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]