[m-dev.] for review: add MC++ implementation of library and runtime

Fergus Henderson fjh at cs.mu.OZ.AU
Mon Dec 18 23:59:13 AEDT 2000


On 18-Dec-2000, Tyson Dowd <trd at cs.mu.OZ.AU> wrote:
> On 05-Dec-2000, Fergus Henderson <fjh at cs.mu.OZ.AU> wrote:
> > On 04-Dec-2000, Tyson Dowd <trd at cs.mu.OZ.AU> wrote:
> > > +EMBEDALLDLLS=$(foreach file,$(ALLDLLS),/embed:$(file),$(patsubst %.dll,%,$(file)),Y)
> > 
> > Hmm, I couldn't parse that.  The `$(foreach ...)' command expects
> > something of the form `$(foreach VAR,LIST,TEXT)', but your foreach
> > command has four commas in it (not counting the ones nested inside `$(patsubst ...)'),
> > not two.
> > 
> > I guess you and/or GNU Make are assuming that TEXT can contain commas.
> > But this isn't documented in the GNU Make manual.
> 
> Yes.
> 
> If you want to embed a resource in a .dll, then you give the option
>         /embed:filename
> 
> If you want to give it a specific name, you have to do
>         /embed:filename,resourcename
> 
> And if you want to make it publically accessible, you have to add a ,Y
>         /embed:filename,resourcename,Y
> 
> GNU Make doesn't allow $(patsubst ... ) rules to do double subtitutions
> (the pattern I want is %.dll,%,Y).
> 
> Microsoft's option handling doesn't allow you to omit the resourcename
> if you want to give a ,Y option.  So you *have* to do a double
> substitution (hence the use of foreach instead of a simple pattern style
> substitution) and you have to put commas in the "text" portion of the
> foreach.

Tyse, Tyse, you sound very defeatist.
All this working on Microsoft products must be getting you down ;-)

You don't have to put commas in the "text" portion of foreach.
You could use e.g. the following

	ALL_DLLS_BASE  = $(ALLDLLS:%.dll=%)
	EMBED_ALL_DLLS = $(foreach dll_name,$(ALL_DLLS_BASE),$(EMBED_ONE_DLL))
	EMBED_ONE_DLL  = /embed:$(dll_name).dll,$(dll_name),Y

which puts the commas in a separate variable.
This technique is even recommended by the GNU Make manual:

|  When TEXT is complicated, you can improve readability by giving it a
|  name, with an additional variable:

Or you could use $(shell) and sed.  Or you could even write a C
program to generate that part of the Makefile.  There's lots of
possibilities.

> GNU Make says nothing about embedded commas (and possible escaping of
> them if it were a problem) but it also never mentions that pattern
> subsitutions are restricted to a single subsitution.
> 
> > > +
> > > +mercury.dll: $(ALLDLLS)
> > > +	al -out:mercury.dll $(ALLDLLS)
> > 
> > If you bother to autoconfigure ILASM, shouldn't you also autoconfigure "al"?
> 
> I only bothered with ILASM to find out the SDK path, so I wasn't going
> to bother with al just yet.

OK.  But it might be good to just define

	AL = al

and then use $(AL) rather than hard-coding `al'.  It probably doesn't
make much difference in this case, but in general it's good style to
avoid hard-coding such names whenever possible.

> > > +:- pragma foreign_code("MC++",
> > > +	get_user_cpu_miliseconds(Time::out), [will_not_call_mercury],
> > > +"
> > > +	// This won't return the elapsed time since program start,
> > > +	// as it begins timing after the first call.
> > > +	// For computing time differences it should be fine.
> > > +	// XXX this is documented but not here.  Perhaps it is fixed
> > > +	// in .NET Frameworks Beta1?
> > > +	// Time = (int) (1000 * Diagnostics::Counter::GetElapsed());
> > > +	MR_Runtime::SORRY(""foreign code for this function"");
> > > +").
> > 
> > The comment here is confusing.
> > What is documented but not here?
> > What do you mean by "here"?
> 
> I got 'method not found' errors.
> 
> It still happens.  I don't know why -- it doesn't seem to find
> 'Diagnostics'.  I will have to spend some time looking into it at some
> point in the future.

Fine, just make the comment clearer.
It's not clear from the comment what the words
"this" and "here" refer to.

> > >  :- impure pred do_nothing(T::in) is det.
> > > -:- pragma c_code(do_nothing(X::in), [will_not_call_mercury, thread_safe], "
> > > +:- pragma foreign_code("C", 
> > > +	do_nothing(X::in), [will_not_call_mercury, thread_safe], "
> > > +	ML_benchmarking_dummy_word = (MR_Word) X;
> > > +").
> > > +:- pragma foreign_code("MC++", 
> > > +	do_nothing(X::in), [will_not_call_mercury, thread_safe],
> > > +"
> > > +	volatile MR_Word ML_benchmarking_dummy_word;
> > >  	ML_benchmarking_dummy_word = (MR_Word) X;
> > >  ").
> > 
> > The comment above says that you assign the output to a volatile global
> > variable, but here you're assigning it to a volatile local variable.
> > 
> > I think it would be safer to assign it to a volatile static variable.
> 
> Can't do that, the MC++ compiler barfs.  
> 
> Another SORRY here until I can look into it futher.

Fine.  Just add an XXX comment explaining it.

> > The numbering of the generic_res_call functions is odd;
> > why is it called `call3', rather than `call0'?
> 
> Because it implements a generic call of an arity 3 procedure with a
> result.  It's a little like call/3.

call/3 has three arguments, but generic_res_call3 has *four*
arguments.  Isn't it more like call/4?

> > > +:- pragma foreign_code("MC++", copy(_X::in, _Y::uo),
> > > +	[may_call_mercury], "
> > > +	MR_Runtime::SORRY(""foreign code for this function"");
> > > +").
> > 
> > I tried to refrain from commenting on calls to SORRY(), but this one
> > looks fairly simple to implement: isn't there a Clone() function or
> > something like that in System.Object?
> 
> Yes, but it does shallow copies.  There are some Clone() methods that do
> 

That do what?
That do shallow copies?

If so, please add an XXX comment mentioning that.

> I've fixed mr_convert and MR_Runtime.  They are now
> mercury::runtime::convert and mercury::runtime::errors.
> 
> I've also replaced the mercury__builtin__ with mercury::builtin::

Fantastic, thanks.

> > >  	% Maximum floating-point number
> > > -:- pragma c_code(float__max = (Max::out),
> > > +:- pragma foreign_code("C", float__max = (Max::out),
> > >  		[will_not_call_mercury, thread_safe],
> > >  	"Max = ML_FLOAT_MAX;").
> > > +:- pragma foreign_code("MC++", float__max = (Max::out),
> > > +		[will_not_call_mercury, thread_safe],
> > > +	"Max = Double::MaxValue;").
> > 
> > Is that System.Double?
> > Is `Double' the right type to use here?
> > I think you should probably be using a typedef of some kind,
> > e.g. `MR_Float'.
> 
> Sounds like a great idea.  The MC++ compiler won't accept it thought.
> I can get it to give an internal compiler error, or it complains that
> mercury::MR_BoxedFloat isn't a class/struct/union.

How about a #define?

> > > +:- pragma foreign_code("MC++", "
> > > +	static MR_Word ML_io_stream_names;
> > > +	static MR_Word ML_io_user_globals;
> > > +	static int next_id;
> > > +").
> > 
> > Hmm...  you're relying here on foreign "MC++" code not being inlined
> > into different translation units (like we do with foreign "C" code).
> > 
> > I think at least it is worth a comment explaining why the MC++
> > version is different from the C version.
> 
> Either I don't understand this comment, or you are confusing
> foreign_code with foreign_decls.
>
> foreign_code cannot be inlined into multiple different translation units.
> It can only appear once.  It has to be put into a single
> translation unit.  No other foreign_code can rely on it being put into
> it's own translation unit (and it could be said that I'm breaking this
> rule because I don't write a separate decl).
> 
> foreign_*decls* can be inlined into multiple different translation units.

I was worried about the foreign_code/4 (i.e. foreign_proc) procedures
that reference that definition being inlined into other translation
units in which the definition is not visible, not about that
definition itself being inlined.  Don't you need a separate
foreign_decl of some kind to handle that case?

Are those those declarations declaring static class members,
or are they static variables at namespace scope?

> > > +static MR_MercuryFile mercury_stdin_binary =
> > > +	new_mercury_file(0, 1);
> > > +static MR_MercuryFile mercury_stdout_binary =
> > > +	new_mercury_file(0, 1);
> > > +
> > > +static MR_MercuryFile mercury_current_text_input =
> > > +	new_mercury_file(Console::OpenStandardInput(), 1);
> > > +static MR_MercuryFile mercury_current_text_output =
> > > +	new_mercury_file(Console::OpenStandardOutput(), 1);
> > > +static MR_MercuryFile mercury_current_binary_input =
> > > +        new_mercury_file(0, 1);
> > > +static MR_MercuryFile mercury_current_binary_output =
> > > +        new_mercury_file(0, 1);
> > 
> > The current_* streams should point to the same objects as
> > mercury_stdin, mercury_stdout, etc., rather than being copies.
> 
> I'll XXX that too.  It's not trivial to arrange this.
> 
> > 
> > > +:- pragma foreign_code("MC++", "
> > > +
> > > +MR_MercuryFile
> > > +static mercury_open(MR_String filename, MR_String type)
> > > +{
> > > +        MR_MercuryFile mf = new MR_MercuryFileStruct();
> > > +        IO::FileMode fa;
> > > +        IO::Stream *stream;
> > > +
> > > +                // XXX get this right...
> > > +        if (type == ""r"") {
> > > +                fa = IO::FileMode::Open;
> > > +        } else if (type == ""w"") {
> > > +                fa = IO::FileMode::Append;
> > > +        } else {
> > > +                fa = IO::FileMode::OpenOrCreate;
> > > +        }
> > 
> > That looks very scary.  I would rather have a call to SORRY() than a
> > file open command that might get things wrong.
> 
> Then a SORRY you shall get.
> 
> I did mention that all this stuff is experimental right, and will as
> likely delete all your files as work? 
> 
> > > +:- pragma foreign_code("MC++", "
> > > +
> > > +static void
> > > +mercury_print_string(MR_MercuryFile mf, MR_String s)
> > > +{
> > > +        IO::StreamWriter *w = new IO::StreamWriter(mf->stream);
> > > +        w->Write(s);
> > > +        w->Flush();
> > 
> > That is likely to be quite inefficient, I'd guess.
> > An XXX comment is warranted.
> 
> Ok.
> 
> > 
> > > +static void
> > > +mercury_print_binary_string(MR_MercuryFile mf, MR_String s)
> > > +{
> > > +        IO::StreamWriter *w = new IO::StreamWriter(mf->stream);
> > > +        w->Write(s);
> > > +        w->Flush();
> > > +}
> > > +
> > > +").
> > 
> > Likewise.
> > 
> > > +static void
> > > +mercury_close(MR_MercuryFile mf)
> > > +{
> > > +        if (mf != mercury_stdin &&
> > > +            mf != mercury_stdout &&
> > > +            mf != mercury_stderr)
> > > +        {
> > > +                mf->stream->Close();
> > > +                mf->stream = 0;
> > > +        }
> > > +}
> > 
> > s/0/NULL/
> 
> This would of course involve #defining NULL.
> 
> It's not currently defined because I'm deliberatley not using the
> "standard" system libraries (just in case they drag in more non .NET
> stuff).

Could you explain that rationale in a bit more detail?
What would be wrong with dragging in more non .NET stuff?
(I don't disagree with you, I'd just like to understand
the issue better.)

<stddef.h> should be pretty safe in that respect.
In ANSI/ISO C and POSIX, it only defines the following:

	macro     NULL
	macro     offsetof()
	typedef   ptrdiff_t
	typedef   size_t
	typedef   wchar_t

ANSI/ISO C requires that system headers like <stddef.h> do not
drag in stuff from other header files into the user's namespace.

You could always use MR_NULL, I suppose.

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
                                    |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list