Here are some of our most popular bugs that we know about in version 1.8; some include fixes. We're busy polishing version 2.0, so this is not as formal as we'd like --- but it's better than nothing.
if ((i = _ilu_Write(self->tr_fd, p->buffer, size + 4)) < size + 4) {Replace it with one that reads
if ((i = _ilu_Write(self->tr_fd, p->buffer, size + 4)), i<0 || ((ilu_cardinal)i) < size + 4) {
if (lastIdx > nIdx) lastIdx = nIdx;into procedures Default_UnregisterInputSource and Default_UnregisterOutputSource in ${ILUSRC}/runtime/kernel/bsdmnlp.c at the places illustrated here:
static ilu_boolean Default_UnregisterInputSource(int fd) { register int i; static ilu_boolean also = ilu_TRUE; if (extra_can_inp != NULLFN) also = (*extra_can_inp) (fd); for (i = 0; i < nIdx; i += 1) if ((IOTab[i].fd == fd) && IOTab[i].input) { nIdx--; if (lastIdx > nIdx) lastIdx = nIdx; if (i != nIdx) { IOTab[i].fd = IOTab[nIdx].fd; IOTab[i].input = IOTab[nIdx].input; IOTab[i].proc = IOTab[nIdx].proc; IOTab[i].rock = IOTab[nIdx].rock; } MaybeDumpIOTab(); return (also); } DEBUG(MAINLOOP_DEBUG, (stderr, "UnregisterInputSource: FD %d not in table!\n", fd)); return (ilu_FALSE); } ... static ilu_boolean Default_UnregisterOutputSource (int fd) { register int i; static ilu_boolean also = ilu_TRUE; if (extra_can_out != NULLFN) also = (*extra_can_out) (fd); for (i = 0; i < nIdx; i += 1) if ((IOTab[i].fd == fd) && !IOTab[i].input) { nIdx--; if (lastIdx > nIdx) lastIdx = nIdx; if (i != nIdx) { IOTab[i].fd = IOTab[nIdx].fd; IOTab[i].input = IOTab[nIdx].input; IOTab[i].proc = IOTab[nIdx].proc; IOTab[i].rock = IOTab[nIdx].rock; } MaybeDumpIOTab(); return (also); } DEBUG(MAINLOOP_DEBUG, (stderr, "UnregisterOutputSource: FD %d not in table!\n", fd)); return (ilu_FALSE); }The second bug is in procedure Default_RunMainLoop in bsdmnlp.c. To fix this bug, add a declaration
struct io_reg lastreg;at the top of the procedure, and change the loop over /lastIdx/ to read as follows:
while (lastIdx > 0) { ilu_boolean forEx, forIo; fd_set *iofds; --lastIdx; forEx = FD_ISSET(IOTab[lastIdx].fd, &excnfds) != 0; iofds = IOTab[lastIdx].input ? &readfds : &writefds; forIo = FD_ISSET(IOTab[lastIdx].fd, iofds) != 0; if (forEx || forIo) { lastreg = IOTab[lastIdx]; DEBUG(MAINLOOP_DEBUG, (stderr, "Default MainLoop: calling IOTab[%d](%d,%d) = {%d, %d, %p, %p}\n", lastIdx, forEx, forIo, lastreg.fd, lastreg.input, lastreg.proc, lastreg.rock)); if (forIo) FD_CLR(lastreg.fd, iofds); (*lastreg.proc) (lastreg.fd, lastreg.rock); goto next; } }
struct wait_frame_s { /* A chained stack frame */ ... ilu_boolean regd; /* is FoundFD still registered? */ };Then make every procedure that calls ilu_ExitMainLoop(&(a struct wait_frame_s).stop) also unregister the I/O handler. There are two such places. One is in TAInvoke:
static void TAInvoke(ilu_Alarmette a) { WaitFrame *wf = (WaitFrame *) a; while (wf->cooler != NIL) wf = wf->cooler; ((wf->input ? ilu_UnregisterInputSource : ilu_UnregisterOutputSource) (wf->fd)); for (wf = wf; wf != NIL; wf = wf->hotter) { wf->sure = wf->regd = 0; ilu_ExitMainLoop(&(wf->stop)); } return; }and the other is in FoundFD:
static void FoundFD(int fd, ilu_private rock) { WaitFrame *wf = (WaitFrame *) rock; /* coolest frame in the stack */ _ilu_Assert(wf->cooler == NIL, "mainloop.c:FoundFD"); ((wf->input ? ilu_UnregisterInputSource : ilu_UnregisterOutputSource) (fd)); for (wf = wf; wf != NIL; wf = wf->hotter) { wf->sure = (wf->hotter == NIL) && (wf->stop == 0); wf->regd = 0; ilu_ExitMainLoop(&(wf->stop)); } return; }Finally, change IOWait to (a) re-register the I/O handler if necessary, and (b) assume the I/O unregistration has already happened by the time ilu_RunMainLoop has returned. It now looks like this:
static void IOWait(int fd, int input, ilu_boolean *sure, ilu_FineTime *limit) { ... this.hotter = NIL; this.regd = TRUE; for (pp = &wfs; (*pp) != NIL; pp = &((*pp)->fd_next) ) { if ( ((*pp)->fd == fd) && ((*pp)->input == input) ) { ... bottom = FALSE; if (this.cooler->regd) goto redy; goto regit; } } this.cooler = NIL; this.fd_next = wfs; wfs = &this; bottom = TRUE; regit: if (input) ilu_RegisterInputSource (fd, FoundFD, (ilu_private) &this); else ilu_RegisterOutputSource(fd, FoundFD, (ilu_private) &this); redy: this.fd = fd; this.input = input; this.stop = 0; this.sure = 2; if (_ilu_DebugLevel & MAINLOOP_DEBUG) { ... } if (limit != NIL) ilu_MXASet(&timeAlarm, &this.wake, *limit); ilu_RunMainLoop(&this.stop); *sure = this.sure; if (limit != NIL) ilu_MXAClear(&timeAlarm, &this.wake); if ( bottom ) { _ilu_Assert(wfs == &this, "IOWait: pop new"); wfs = this.fd_next; } else { _ilu_Assert(this.cooler != NIL, "IOWait: this.cooler == NIL"); _ilu_Assert(this.fd_next == this.cooler->fd_next, "IOWait: pop old"); *pp = this.cooler; (*pp)->hotter = NIL; } return; }
static ilu_boolean Default_UnregisterInputSource(int fd) { register int i; static ilu_boolean also = ilu_TRUE; if (extra_can_inp != NULLFN) also = (*extra_can_inp) (fd); FD_CLR(fd, &readfds); FD_CLR(fd, &excnfds); for (i = 0; i < nIdx; i += 1) ... } ... static ilu_boolean Default_UnregisterOutputSource (int fd) { register int i; static ilu_boolean also = ilu_TRUE; if (extra_can_out != NULLFN) also = (*extra_can_out) (fd); FD_CLR(fd, &writefds); for (i = 0; i < nIdx; i += 1) ... }
static ilu_Mooring _ilu_tcp_CreateMooring (ilu_private mooringInfo) { int oflags; ... if (listen(skt, 4) < 0) { ... } if ((oflags = fcntl(skt, F_GETFL, 0)) < 0 || fcntl(skt, F_SETFL, oflags | O_NDELAY) < 0) { int theerr = errno; DEBUG((EXPORT_DEBUG | TCP_DEBUG), (stderr, "_tcp_CreateMooring: fcntl(%d, F_SETFL, oflags|O_NDELAY) failed: %s.\n", skt, strerror(theerr))); close(skt); return (NIL); } self = (ilu_Mooring) ilu_must_malloc (sizeof(struct _ilu_Mooring_s)); ... }This should work for SunOS 4.1, Solaris 2, and Linux; for other systems you may need a different flag or an entirely different way to set the socket non-blocking. Second, react properly when accept() returns due to non-blocking:
static ilu_Transport _tcp_AcceptClient (ilu_Mooring self) { ... if ((ns = accept(self->mo_fd, (struct sockaddr *) &sin, &addrlen)) < 0) { if (errno == EWOULDBLOCK) fprintf(stderr, "tcp: accept(%d) would block!\n", self->mo_fd); else perror ("_tcp_AcceptClient: accept"); } else ... }Again, this should work for SunOS 4.1, Solaris 2, and Linux; for other systems, you may need to compare against another errno (EAGAIN would be a good guess --- EAGAIN should work for Solaris 2 and Linux, but not SunOS 4.1). Actually, the fprintf only helps you detect that this situation has arisen. Once that's no longer a mystery, you needn't bother printing the message.
What this means is that after a connectivity break between client and server, the client may henceforth get errors --- even after connectivity is restored.
The fix for this one is not small. Below are both a complete workaround and a partial fix; the workaround should work regardless of how much you fix the problem.
To work around this bug, do the following. On the client, react to the first call failure by banking the surrogate server and dissociating all its objects (in C, e.g., this can be done by a call on ILU_C_DestroyObjectAndServer, if you're using only one object from that server), and then re-importing the desired objects when you want them. The importation and/or subsequent uses should succeed or fail depending on whether connectivity is now available.
To partially fix the problem, add the following declaration into $ILUSRC/runtime/kernel/iluntrnl.h:
/*L1.sup < cmu; L2 >= {conn's callmu, iomu}*/ void _ilu_CloseLostConnection(ilu_Connection conn);Between _ilu_ClearConnectionFromServer and _ilu_NDataPresent would be a plausible place. Add the definition to connect.c:
/*L1.sup < cmu; L2 >= {conn's callmu, iomu}*/ void _ilu_CloseLostConnection(ilu_Connection conn) { ilu_Server s = conn->co_server; DEBUG(CONNECTION_DEBUG, (stderr, "Closing lost connection %p\n", conn)); _ilu_AcquireMutex(ilu_cmu); _ilu_AcquireMutex(server_lock(s)); _ilu_CloseIoingConnection(conn, FALSE); _ilu_ReleaseMutex(server_lock(s)); _ilu_ReleaseMutex(ilu_cmu); return; }between _ilu_CloseConnection and ilu_CloseConnection. Add a call on the new procedure, from a new "if" statement in ilu_FinishRequest in call.c:
ilu_boolean ilu_FinishRequest (ilu_Call call) { if (call_connection(call) == NIL) { _ilu_CommunicationsError (call, errno, CONNECTION_ERROR); return FALSE; } else { ilu_boolean ans = protocol_finish_request(call_proto(call), call); if ( (!call_transport(call)->tr_class->tc_timesout) || method_asynchronous(call_method(call)) /* ACK! */ ) protocol_drop_request(call_proto(call), call); if (!ans) _ilu_CloseLostConnection(call_connection(call)); return (ans); } }A complete fix would add such calls in many more places.
This can lead to memory smashes, and thus arbitrary misbehavior.
To fix, in procedure _sunrpc_SendPacket in ${ILUSRC}/runtime/kernel/sunrpc.c, replace the line
ilu_free(ev->buffer); /* pretty useless by now */with
#if 0 ilu_free(ev->buffer); /* pretty useless by now */ /* ... but not owned by this code! */ #endifAnd if your installation includes courier, do the analogous fix to _courier_SendPacket in courier.c.
err = IUpdate(obj, t); ILU_MUST_BE_SUCCESS(err);in procedure ilu_SetLSO in ${ILUSRC}/runtime/kernel/object.c with
err = IUpdate(obj, t); ILU_ERR_SWITCH (err) { ILU_SUCCESS_CASE {} ILU_ERR_CASE(GcRegFailed, e) _ilu_Assert(lso == NIL, "Couldn't register GC interest in collectible instance (in ilu_SetLSO)"); ilu_FreeError(err); } ILU_ERR_ENDSWITCH;
static ilu_MainLoop kml = { CallRun, CallExit, CallRegisterInput, CallUnregisterInput, CallRegisterInput, CallUnregisterInput, CallCreateAlarm, CallSetAlarm, CallUnsetAlarm };with
static ilu_MainLoop kml = { CallRun, CallExit, CallRegisterInput, CallUnregisterInput, CallRegisterOutput, CallUnregisterOutput, CallCreateAlarm, CallSetAlarm, CallUnsetAlarm };
Subject: old C++ runtime bug still there... Message-ID: "<199509131850.UAA04246@les-rouies.grenoble.rxrc.xerox.com>" From: Steve.Freeman@grenoble.rxrc.xerox:com:Xerox Date: Wed, 13 Sep 1995 11:50:55 PDT This one turned up a while ago, and I've bumped into it again now we've been using ILU some more. I'm going to fix our copy to duplicate the id twice at line (3). steve ------- start of forwarded message (RFC 934 encapsulation) ------- Message-Id: <9409161252.AA04014@grenoble.rxrc.xerox.com> From: freeman@meije (Steve Freeman) Subject: why we need garbage collection... Date: Fri, 16 Sep 1994 14:52:46 --100 In the c++ runtime there are two instance handle fields: one in the C++ object and one in the ILU object. These are set up by the following code: ilu_KernelObject iluObject::ILUEnsureKernelObject() { if (this->ILURPCObject == NULL) { /* Should be a true object. */ static int idcounter = 0; char * id; this->ILURPCServer = this->ILUGetServer()->KernelServer(); if (this->ILUInstanceClassRecord->cl_singleton) (1) id = "0"; else if ((id = this->ILUGetInstanceHandle()) != NULL) (2) id = _ilu_Strdup(id); else { char idbuf[10]; sprintf(idbuf, "%u", ++idcounter); (3) this->ILUInstanceHandle = id = _ilu_Strdup(idbuf); } [etc...] In case (1), the instance handle "id" is constant so it's generated on the fly. In case (2), the instance handle is _copied_ from an existing C++ one. In case (3), the instance handle fields for both objects point to the same storage. When destroying the object, the instance handles for both the C++ and ILU objects are freed, which is obviously a bug in case (3). Generally, this is not a problem, because you can get away with freeing space more than once and in the original, single-threaded, C++ runtime, this storage is not reused until we return from both frees. In my new, improved, multi-threaded C++ runtime, however, the storage can very occasionally be allocated to something else between the two frees, with obvious (well, not really) results. This stuff is just so much _fun_... steve ------- end -------
Subject: Re: Unregistering Sources in runtime/kernel/tcp.c In-Reply-To: "<95Sep7.105337pdt."17062(2)"@alpha.xerox.com>" Message-ID: "<199509071843.UAA00575@galibier.grenoble.rxrc.xerox.com>" To: Mike Spreitzer:PARC Cc: Steve.Freeman@grenoble.rxrc.xerox Reply-To: freeman@xerox:fr From: Steve.Freeman@grenoble.rxrc.xerox:com:Xerox Received: from meije.grenoble.rxrc.xerox.com ([13.202.220.10]) by alpha.xerox.com with SMTP id <19070(10)>; Thu, 7 Sep 1995 11:42:54 PDT Received: from galibier.grenoble.rxrc.xerox.com (galibier [13.202.220.35]) by meije.grenoble.rxrc.xerox.com (8.6.10/8.6.9) with ESMTP id UAA06231; Thu, 7 Sep 1995 20:43:12 +0200 Original-From: Steve FreemanReceived: (freeman@localhost) by galibier.grenoble.rxrc.xerox.com (8.6.12/8.6.9) id UAA00575; Thu, 7 Sep 1995 20:43:11 +0200 Date: Thu, 7 Sep 1995 11:43:11 PDT References: "<199509070956.LAA06712@goelon.grenoble.rxrc.xerox.com>", "<95Sep7.105337pdt."17062(2)"@alpha.xerox.com>" Mike Spreitzer writes: > Actually, look at the code in frame 4. You'll see that it expected you to pass > NULL function pointers, instead of procedures that crash. Ah but! look at the code in frame 3 (CallUnregisterInput, in iluServer.cc). The C++ runtime defines mainLoop as an abstract class which should be made concrete and then registered. The runtime then registers its own private mainLoop functions that pass the calls through to the relevant methods of this object. For now, I'm just returning TRUE, but maybe I should just drop the C++ main loop and use the C one? It feels like the C++ version of mainLoop should either be annotated or removed. > We don't need documentation --- we distribute source code! ;-) Which is always an education to read... steve
Date: Thu, 07 Sep 95 13:30:13 PDT From: Mike Spreitzer:PARC:Xerox Subject: Re: Unregistering Sources in runtime/kernel/tcp.c Oh damn. Yes, it looks like the C++ runtime is inconsistent with what the kernel expects. Sorry 'bout that. You could register a C++ mainloop with ml_register_X procs that crash, ml_unregister_X procs that just return TRUE. Or you could register a C mainloop.
ASYNCHRONOUS deliverData( IN context: ServerContextID, IN messageID: MessageID, IN data: OctetSeq),The following patch to ILUSRC/stubbers/parser/iluparse.c.dist fixes the problem:
*** 1.1 1995/10/18 02:04:58 --- iluparse.c.dist 1995/10/18 02:05:39 *************** *** 4186,4191 **** --- 4186,4193 ---- while (c == '(') { c = GetChar(file); + if (c == NEWLINE) + CurrentParse->line += 1; if (c == '*') { c = EatComment (file);
fprintf (s->context->file, "#define %s__%s ", c_type_name (s->context->class), type_name (a->type));with
fprintf (s->context->file, "#define %s__%s ", c_type_name (s->context->class), c_string (type_name (a->type)));
Subject: Output CString arguments and C clients Message-ID: "<199509291636.QAA17155@qvarsx.er.usgs.GOV>" From: jfulton@usgs:gov:Xerox Date: Fri, 29 Sep 1995 09:41:45 PDT I'm having trouble calling functions that use output ilu.CString arguments from C. I believe that this is due to a bug in the C code generated. Take the trivial interface, spam.isl: INTERFACE spam; TYPE Factory = OBJECT DOCUMENTATION "spam spam spam" METHODS f( OUT i : INTEGER, OUT m : ilu.CString, OUT j : INTEGER ) "get spammed" END; I have no trouble getting this to work with a Python server and client, however, when I write a C client: #include#include static spam_Factory ilu_factory=0; static int initFactory() { if(! ilu_factory) { char sbh[128]; char *server="nwiqqvarsa.er.usgs.gov"; char *protocol="844852514"; int port=5000; spam__Initialize( ); sprintf(sbh, "theFactory@spam.%s" "@sunrpc_2_0x31000400_%s" "|tcp_130.11.50.6_%d", /* I'm not done here */ server,protocol,port); ilu_factory=spam_Factory__CreateFromSBH(sbh,NULL); if (ilu_factory == NULL) { fprintf (stderr, "Couldn't find Factory object on server, %s.\n", server); return 0; } } return 1; } main() { char *d="client"; ILU_C_ENVIRONMENT env; ilu_integer i= -1,j= -1; if(! initFactory()) { printf("could not get factory\n"); return; } spam_Factory_f(ilu_factory, &env, &i, &d, &j); if (! ILU_C_SUCCESSFUL(&env)) { printf("call failed\n"); return; } printf("%d %d\n",i,j); if(d) printf("%s\n",d); else printf("No data\n"); } The value of the string argument is unchanged. I looked at the code in spam-surrogate.c that recieves the output arguments, which had: { ilu_cardinal _len; m = ILU_NIL; ilu_InputString(_call, (ilu_CString *) &m, &_len, 0, ilu_FALSE); } Now, m is already a ilu_CString *, so I see no reason for the "&". ... Jim
Subject: memory leak on inout strings? Message-ID: "<9506221924.AA08832@cen.com>" From: pm@fox.cen:com:Xerox Date: Thu, 22 Jun 1995 12:24:34 PDT Looking at the generated C++ code (ILU version 1.8), I see that for a string declared inout, the previous value of the string is not freed before the proxy method gets the returned value via ilu::InputString. Thus, to do things correctly, the client must: ilu_T_CString s = strdup ("input string"); ilu_T_CString save_s = s; // save for future destruction obj->aMethod (&s); // pass inout argument free (save_s); // clean up previous value of s I think this problem is (a) a memory leak for which there may be no fix, and/or (b) a topic worthy of some documentation. Regards -- Phil Miller
Subject: C++-Stubber Bug Message-ID: Originator: "Ken Feuerman:PARC:Xerox", UniqueString: "28-Jul-95 8:24:15" From: Ken Feuerman:PARC:Xerox Date: Fri, 28 Jul 1995 08:24:19 PDT Given the following .ISL file: INTERFACE testbug BRAND "v2.0" IMPORTS ilu END; TYPE Interval = ARRAY of 2 CARDINAL; TYPE IntervalSeq = SEQUENCE of Interval; TYPE Intervals = OPTIONAL IntervalSeq; the resulting C++ code generated in the client stubs won't compile. There seem to be a couple of type declaration "typos", and a problem with assignment into an array value. --Ken.and
Subject: Runtime Bug in C++ Stub Message-ID: Originator: "Ken Feuerman:PARC:Xerox", UniqueString: "3-Aug-95 10:28:39" From: Ken Feuerman:PARC:Xerox Date: Thu, 3 Aug 1995 10:28:46 PDT There's a runtime bug in the C++ stub generated when declaring an OPTIONAL SEQUENCE type. Given the following ISL: ------------------------------------ INTERFACE testbug BRAND "v2.0" IMPORTS ilu END; TYPE Interval = RECORD startPos : CARDINAL, endPos : CARDINAL END; TYPE IntervalSeq = SEQUENCE of Interval; TYPE Intervals = OPTIONAL IntervalSeq; ------------------------------------- An excerpt of the client-common stub reads as follows: ------------------------------------- void testbug_G::Free_Intervals (testbug_T_Intervals _val) { testbug_G::Free_IntervalSeq (_val); } void testbug_G::Free_IntervalSeq (testbug_T_IntervalSeq _val) { register testbug_T_Interval *data = _val->Array(); free((char *) data); } --------------------------------------- Any variable foo of type Intervals could be NULL, since it's OPTIONAL. But if/when testbug_G::Free_Intervals(foo) is called, there's a de-reference of NULL in testbug_G::Free_IntervalSeq(). --Ken.
Subject: array as return value in C++ Message-ID: "<9505092021.AA01675@cacao.wrc.xerox.com>" To: ilu-bugs:PARC Date: Tue, 9 May 1995 13:21:53 PDT I have the following interface: INTERFACE foo; TYPE R3 = ARRAY OF 3 REAL; TYPE bar = OBJECT METHODS init (), run () : R3 END; The code produced by the C++ stubber contains a syntax error in the handling of the return value of method "run". The code is below. I have marked the part of the generated code which the compiler complains about with lines starting with "*********". I am running on SunOS4.1.3 using the sunpro-2.0 compilers. Let me know if you need any more info. Thanks, --------------------------------------------------------------- Mitch Garnaat Wilson Center for Research & Technology garnaat@wrc.xerox.com Xerox Corporation (716)422-1521 800 Phillips Road MS 128/28E (716)265-7133 (fax) Webster, NY 14580 /* This file was automatically generated with ILU (version 1.8) tools * at Tue May 9 15:09:06 1995 by `garnaat' * running "/local/packages/ilu/bin/c++-stubber" of Tue May 9 14:07:47 1995 * on "/local/garnaat/ilu/foo.isl" of Mon May 8 23:39:59 1995, * and "/local/packages/ilu/interfaces/ilu.isl" of Tue May 9 14:07:45 1995. * * ILU is Copyright 1991-1995 Xerox Corporation, All Rights Reserved. * ILU information: ftp://ftp.parc.xerox.com/pub/ilu/ilu.html. */ #include#include extern "C" { #include }; ... foo_T_R3 * foo_T_bar::run (fooStatus *_status) { foo_T_R3 * _retvalue; ilu_Call _call; ilu_Cardinal _sizeOfDiscriminator = 0; ilu_ProtocolException _perror; ilu_Cardinal _scode; if ((_call = ilu_BeginCall (this->ILUGetKernelServer())) == NULL) { _status->returnCode = ilu::ProtocolError; _status->values.anyvalue = (ilu_Cardinal) ilu_ProtocolException_LostConnection; return ((foo_T_R3 *) 0); }; _sizeOfDiscriminator = ilu::SizeOfObjectID(_call, this->ILUGetRPCObject(), ilu_TRUE, NULL); ilu::BeginRequest (_call, foo_T_bar::ILUClassRecord, MethodRecord_foo_T_bar_run, (0+_sizeOfDiscriminator)); ilu::OutputObjectID (_call, this->ILUGetRPCObject(), ilu_TRUE, NULL); ilu::FinishRequest (_call); if ((_perror = ilu::WaitForReply (_call, &_scode)) == ilu_ProtocolException_Success) { if (_scode == 0) { _status->returnCode = NULL; ********* error on the following line ********* compiler output is: "foo.cc", line 213: error: bad assignment type: foo_T_R3* = ilu_Real* _retvalue = (ilu_Real *) foo_G::Input_R3 (_call, NULL); ********* }; /* no exceptions to catch */ } else { _status->returnCode = ilu::ProtocolError; _status->values.anyvalue = (ilu_Cardinal) _perror; }; ilu::FinishCall (_call); return(_retvalue); } ...
ilu::OutputOpaque(_call, ((_retvalue == NULL) ? NULL : *_retvalue), 8)where
typedef unsigned char ilu_Byte; ... typedef ilu_Byte Test1_T_A0[8]; ... Test1_T_A0 * _retvalue;and the second argument to "ilu::OutputOpaque" is declared to be an "unsigned char *". The problem is the type of the second actual argument in the given code fragment. g++ on Linux has been observed to decide that the type of the actual argument is "void *", which cannot be assigned to an "unsigned char *". Note that "NULL" is #defined by system header files, usually to "0" or "((void*)0)". A more clearly unobjectionable pattern would be to use an explicit cast on the null pointer in the conditional expression:
ilu::OutputOpaque(_call, ((_retvalue == NULL) ? (ilu_Byte*)0 : *_retvalue), 8)