gap icon indicating copy to clipboard operation
gap copied to clipboard

analog of ViewObj that stores output in a memory area

Open dimpase opened this issue 5 years ago • 9 comments

For interfacing with libgap, one wants to be able to store the output of ViewObj() called on a GAP object in a memory area, rather than being sent to stdout.

To explain by analogy: if ViewObj() is akin to printf(), then I'd like to have something like sprintf().

Ideally, I'd rather not be doing memory management on that memory area (unlike how it's done for sprintf(), but this is of secondary importance).


In Sagemath's current libGAP this is worked out by replacing stdout with such a memory area, but it's clear that it's very far for ideal, as it needs patches to GAP kernel.

dimpase avatar Sep 19 '18 16:09 dimpase

There is an Operations ViewString which is supposed to do exactly this, so one solution might be to add ViewString methods to more objects (after which they usually no longer need ViewObj methods).

stevelinton avatar Sep 21 '18 11:09 stevelinton

What I am thinking of using for the time being is

    str = NEW_STRING(0);
    out = CALL_2ARGS(   /* setting a string buffer */
       GAP_ValueGlobalVariable("OutputTextString"), str, True);
     ...
    CALL_2ARGS(GAP_ValueGlobalVariable("PrintTo"), out, foo);
    CALL_2ARGS(GAP_ValueGlobalVariable("AppendTo"), out, baz);
     ...

and then CSTR_STRING(str) contains what I need. This does not strike me as optimal, though. (And perhaps there are pitfalls here I don't foresee).

dimpase avatar Sep 21 '18 13:09 dimpase

Note that @ThomasBreuer seems to think differently about ViewObj vs ViewString; Also note that PrintTo does not do the same thing as ViewObj. A potenially usefuel (yet again) hack would be implementing ViewTo which works like PrintTo.

markuspf avatar Sep 25 '18 14:09 markuspf

@stevelinton We had a discussion on ViewString during the recent GAP Days in Siegen. My point of view (or View?) is the following. I had recently tried to add some ViewString methods in order to achieve the ViewObj output for certain GAP objects, and came to the conclusion that this approach is not sensible. Note that ViewString is defined to return

a string that which would be displayed by ViewObj.

A natural and simple way to achieve this would be a (kernel) function that calls ViewObj and redirects the printed stuff to a string. If I understand @markuspf right then ViewTo would do this, and thus would solve the problem from this issue.

However, ViewString is different. It is an operation, and the manual section View and Print says that ViewString methods must not delegate to ViewObj, whereas the delegation from ViewObj to ViewString is allowed. (I think this was not a good idea. Note that ViewString is around for quite some time, but many GAP objects have no sensible ViewString method. Perhaps other people ran into the same problems as I when they tried to use ViewString.)

Where is the problem? In my eyes, an important aspect of ViewObj is to break the printed stuff into lines. (If we do not agree on that then there is really a problem --I do not want to adjust hundreds of pages of GAP examples in order to reflect a changed behavior of GAP.) GAP does this via a heuristic formula, see the kernel function PutChrTo, in the file src/io.c. Another ingredient is the kernel function SET_PRINT_OBJ_INDEX; it is used in the ViewObj method for finite lists which deals for example with matrices and lists of matrices. I do not see how we can get the same behavior with ViewString methods which we currently have with ViewObj methods.

ThomasBreuer avatar Oct 01 '18 14:10 ThomasBreuer

In Sage's interface to GAP (and the Sage-independent Python library I'm working based on the Sage interface) this issue is currently the only reason it needs to use the internal functions OpenOutputStream() and CloseOutput(). Although it might still be useful to expose these in the libgap API, I wouldn't need them except for this.

The only way to consistently capture the ViewObj output is to set the output stream to a text stream, call ViewObj(), then CloseOutput(). A ViewTo or ViewObjTo (however you want to name it) global function which does effectively the same thing would also alleviate this problem.

It's unfortunate that (for understandably historical reasons) that ViewString and ViewObj are two completely independent operations that can, if they want, do complete different things. A sensible thing to do would be to implement ViewString and have ViewObj delegate to it, and that is done for some objects in the standard library, but of course this is not done consistently across the GAP extended universe so can't be relied upon.

embray avatar Jan 14 '21 09:01 embray

GAP.jl basically does the same thing, but defines a new GAP function STREAM_CALL for this purpose. Having this in GAP itself could also be useful: https://github.com/oscar-system/GAP.jl/blob/2d383fcecd48bbcc90b520cf6dec3217175a76c0/pkg/JuliaInterface/src/JuliaInterface.c#L320

embray avatar Jan 14 '21 09:01 embray

The short version is indeed the relationships between functions like ViewObj and ViewString and friends should be clearer, with clear delegations, but previously attempts to "clean this up" have suffered from there just been too much code, and also because GAP package tests are typically based around matching strings, cleanups which introduce changes in how objects print lead to breaking lots of tests.

ChrisJefferson avatar Jan 14 '21 11:01 ChrisJefferson

There is now CALL_WITH_STREAM in the GAP kernel (and it will be in GAP 4.12, to be released tomorrow). This is based on STREAM_CALL from GAP.jl (and GAP.jl already has been using CALL_WITH_STREAM for some time now).

fingolfin avatar Aug 17 '22 00:08 fingolfin

OK, thanks.

dimpase avatar Aug 18 '22 22:08 dimpase

I'm trying to revive gappy, and I am again facing this issue; e.g. I'd like to replace the old 1-argment version of OpenOutputStream with something equivalent.

Any pointers? Can this be done with documented libgap API?

dimpase avatar May 07 '24 19:05 dimpase

from GAP.jl:

BindGlobal( "StringDisplayObj",
function(obj)
  local str, out;
  str := "";
  out := OutputTextString(str, false);
  SetPrintFormattingStatus(out, false);
  CALL_WITH_STREAM(out, Display, [obj]);
  CloseStream(out);
  return str;
end );

BindGlobal( "StringViewObj",
function(obj)
  local str, out;
  str := "";
  out := OutputTextString(str, false);
  SetPrintFormattingStatus(out, false);
  CALL_WITH_STREAM(out, ViewObj, [obj]);
  CloseStream(out);
  return str;
end );

fingolfin avatar May 07 '24 20:05 fingolfin

hmm, that's certainly not libgap API, it's just straight GAP, involving a undocumented GAP function CALL_WITH_STREAM. Any reason it's not in GAP itself?

dimpase avatar May 07 '24 21:05 dimpase

The function CALL_WITH_STREAM is "undocumented" alright, but it was introduced for just this purpose. And it is in "GAP itself", so I don't really understand your question.

If you'd like a single libgap function for doing what you want: PR welcome.

fingolfin avatar May 08 '24 06:05 fingolfin