How to fix error 177 ???

General FreeBASIC programming questions.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: How to fix error 177 ???

Post by Munair »

Comparing the local object's address inside the function and the address of the object after the function's result assignment, they appear to be the same. From your remarks, I conclude that this is no guarantee and in order to guarantee it, the *new* keyword should be used in the function.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to fix error 177 ???

Post by fxm »

Yes.

For the bugged code, the compiler does not complain because a hacked expression is used to create first the null reference:
dim byref t as TT = *cptr(TT ptr, 0)
@t = @TT()


If you attempt to directly create a reference on a temporary object:
dim byref t as TT = TT()
"error 24: Invalid data types" is outputted by the compiler.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: How to fix error 177 ???

Post by Munair »

fxm wrote:Yes.

For the bugged code, the compiler does not complain because a hacked expression is used to create first the null reference:
dim byref t as TT = *cptr(TT ptr, 0)
@t = @TT()


If you attempt to directly create a reference on a temporary object:
dim byref t as TT = TT()
"error 24: Invalid data types" is outputted by the compiler.
I was under the impression that the "invalid data types" error is due to the byref clause, not because the object is temporary.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: How to fix error 177 ???

Post by Munair »

For example, I can setup a null reference object with and without *byref*:

Code: Select all

dim t as TT = *cptr(TT ptr, 0)
' or
dim byref t as TT = *cptr(TT ptr, 0)
But a direct call to the (default) constructor using *byref*, as you pointed out, is not allowed:

Code: Select all

dim byref t as TT = TT()
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to fix error 177 ???

Post by fxm »

Munair wrote:I was under the impression that the "invalid data types" error is due to the byref clause, not because the object is temporary.
Not exactly.

With a local instance, no compiler error but the program does not run as wanted:

Code: Select all

type TT extends object
   s as string
   declare constructor()
end type

constructor TT()
   print "TT called"
end constructor

function SomeTT(byval b as boolean = false) byref as TT
   dim t0 as TT
   dim byref t as TT = t0
   t.s = "Hello"
   return t
end function

print (SomeTT(true)).s
sleep
With a dynamic or static for example instance, the program run well:

Code: Select all

type TT extends object
   s as string
   declare constructor()
end type

constructor TT()
   print "TT called"
end constructor

function SomeTT(byval b as boolean = false) byref as TT
   static t0 as TT
   dim byref t as TT = t0
   t.s = "Hello"
   return t
end function

print (SomeTT(true)).s
sleep
sancho3
Posts: 358
Joined: Sep 30, 2017 3:22

Re: How to fix error 177 ???

Post by sancho3 »

@mrminecrafttnt
There is another alternative that I like to use.
When there is a chance that a function may return an unusable result, set it up as a boolean return value and use either a pointer to the desired return type or a byref parameter.
Return true when the return value is valid and false when not.
Here is the byref param version:

Code: Select all

function Inventory.getObj(ObjName as string, Byref iod As InventoryObjectData) as Boolean 
	dim as integer id = search_id(ObjName)
	if id = -1 then
	  print "Object not found"
	  Return False 
	Else
		iod = ObjectData(id)
		return true 
	end if
End Function

and here is the pointer version:

Code: Select all

function Inventory.getObj(ObjName as string, Byval pIOD As InventoryObjectData Ptr) as Boolean 
	dim as integer id = search_id(ObjName)
	if id = -1 then
	  print "Object not found"
	  Return False 
	Else
		pIOD = @ObjectData(id)
		return true 
	end if
End Function
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: How to fix error 177 ???

Post by Munair »

Or, if you really only want to have one function output, in the case of types/objects, add a boolean member as flag to indicate the object is valid/found.
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: How to fix error 177 ???

Post by paul doe »

In addition to what sancho3 and Munair propose, here's a simple scheme that I use often:

Code: Select all

/'
  The ID you'll use to identify objects throughout your
  code base.
'/
type ObjectID as string

/'
  The abstract interface for an inventory item
'/
type IInventory extends Object
  public:
    declare virtual destructor()
    declare virtual property ID() as ObjectID
  
  protected:
    declare constructor()
    declare constructor( _
      byref as const ObjectID )
  
  private:
    m_ID as ObjectID
end type

constructor IInventory()
  this.constructor( "<Unknown>" )
end constructor

constructor IInventory( _
  byref anID as const ObjectID )
  
  m_ID = anID
end constructor

destructor IInventory()
end destructor

property IInventory.ID() as ObjectID
  return( m_ID )
end property

/'
  A concrete implementation of an inventory item
'/
type Asset extends IInventory
  public:
    declare constructor( _
      byref as const ObjectID )
    declare destructor() override
    
    static Null as Asset ptr
  
  private:
    declare constructor()
end type

'' The null asset
dim as Asset ptr _
  Asset.Null = cptr( Asset ptr, 0 )

constructor Asset()
  base()
end constructor

constructor Asset( _
  byref anID as const ObjectID )
  
  base( anID )
end constructor

destructor Asset()
end destructor

/'
  Finds an asset on a given array. Returns Asset.Null if
  it's not found.
'/
function findAsset( _
  assets() as Asset, _
  byref anID as const ObjectID ) as Asset ptr
  
  for _
    i as integer = lbound( assets ) to ubound( assets )
    
    if( _
      assets( i ).ID = anID ) then
      return( @assets( i ) )
    end if
  next
  
  return( Asset.Null )
end function

/'
  Main code
'/
dim as Asset _
  someAssets( ... ) = _
  { _
    Asset( "Pears" ), _
    Asset( "Apples" ), _
    Asset( "Bananas" ) _
  }
/'
  See if we can find some asset
'/
var anAsset = findAsset( someAssets(), "Apples" )

if( _ '' Found it?
  anAsset <> Asset.Null ) then
  /'
    Do some mighty interesting stuff
  '/
  ? "Found it!"
else
  '' Crap out
  ? "Couldn't find it!"
end if

sleep()
In other words, don't use references when working with objects unless you absolutely have to. Since in FreeBasic you can't do late binding of references, there's not much else you can do when/if you start working with interfaces.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How to fix error 177 ???

Post by fxm »

paul doe wrote:Since in FreeBasic you can't do late binding of references, there's not much else you can do when/if you start working with interfaces.
In FreeBASIC, anything that can be done with pointers can be done with references (because a reference is the dereferencing of an internal pointer, but accessible by user).

Your example modified for using references:

Code: Select all

/'
  The ID you'll use to identify objects throughout your
  code base.
'/
type ObjectID as string

/'
  The abstract interface for an inventory item
'/
type IInventory extends Object
  public:
    declare virtual destructor()
    declare virtual property ID() as ObjectID
 
  protected:
    declare constructor()
    declare constructor( _
      byref as const ObjectID )
 
  private:
    m_ID as ObjectID
end type

constructor IInventory()
  this.constructor( "<Unknown>" )
end constructor

constructor IInventory( _
  byref anID as const ObjectID )
 
  m_ID = anID
end constructor

destructor IInventory()
end destructor

property IInventory.ID() as ObjectID
  return( m_ID )
end property

/'
  A concrete implementation of an inventory item
'/
type Asset extends IInventory
  public:
    declare constructor( _
      byref as const ObjectID )
    declare destructor() override
   
    static byref Null as Asset
 
  private:
    declare constructor()
end type

'' The null asset
dim byref as Asset _
  Asset.Null = *cptr( Asset ptr, 0 )

constructor Asset()
  base()
end constructor

constructor Asset( _
  byref anID as const ObjectID )
 
  base( anID )
end constructor

destructor Asset()
end destructor

/'
  Finds an asset on a given array. Returns Asset.Null if
  it's not found.
'/
function findAsset( _
  assets() as Asset, _
  byref anID as const ObjectID ) byref as Asset
 
  for _
    i as integer = lbound( assets ) to ubound( assets )
   
    if( _
      assets( i ).ID = anID ) then
      return( assets( i ) )
    end if
  next
 
  return( Asset.Null )
end function

/'
  Main code
'/
dim as Asset _
  someAssets( ... ) = _
  { _
    Asset( "Pears" ), _
    Asset( "Apples" ), _
    Asset( "Bananas" ) _
  }
/'
  See if we can find some asset
'/
var anAsset = findAsset( someAssets(), "Apples" )

if( _ '' Found it?
  @anAsset <> @Asset.Null ) then
  /'
    Do some mighty interesting stuff
  '/
  ? "Found it!"
else
  '' Crap out
  ? "Couldn't find it!"
end if

sleep()
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: How to fix error 177 ???

Post by Munair »

Why would Null be part of the object?

Code: Select all

#define nil(o)	(varptr(o) = 0)
...
if not ( _ '' Found it?
 Nil(anAsset) ) then
Even better would be a built-in keyword Nil in the category New and Delete:

Code: Select all

if anAsset <> Nil then
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: How to fix error 177 ???

Post by paul doe »

fxm wrote:In FreeBASIC, anything that can be done with pointers can be done with references (because a reference is the dereferencing of an internal pointer, but accessible by user).
Not true, because there's no orthogonality between references and pointers:

Code: Select all

type ISomething extends Object
  declare virtual destructor()
  declare abstract property foo() as integer
end type

destructor ISomething()
end destructor

type SomeObject
  public:
    declare constructor( _
      byref as ISomething )
    declare destructor()
  
  private:
    declare constructor()
    
    m_something as ISomething
end type

constructor SomeObject()
end constructor

constructor SomeObject( _
  byref aReference as ISomething )
  
  m_something = aReference
end constructor

destructor SomeObject()
end destructor
In this case, how we can inject a reference to an interface? I don't want a copy, I want a reference (managed by another scope). The closest you'll ever get in FreeBasic is this:

Code: Select all

type ISomething extends Object
  declare virtual destructor()
  declare abstract property foo() as integer
end type

destructor ISomething()
end destructor

type SomeObject
  public:
    declare constructor( _
      byref as ISomething )
    declare destructor()
  
  private:
    declare constructor()
    
    m_something as ISomething ptr '' Notice it's a pointer
end type

constructor SomeObject()
end constructor

constructor SomeObject( _
  byref aReference as ISomething )
  
  m_something = @aReference
end constructor

destructor SomeObject()
end destructor
Might as well just work with pointers and avoid these issues altogether. If FreeBasic allowed late binding, this would be written as:

Code: Select all

type ISomething extends Object
  declare virtual destructor()
  declare abstract property foo() as integer
end type

destructor ISomething()
end destructor

type SomeObject
  public:
    declare constructor( _
      byref as ISomething )
    declare destructor()
  
  private:
    declare constructor()
    
    m_something as ISomething = any '' Equivalent to 'nothing' in other languages
end type

constructor SomeObject()
end constructor

constructor SomeObject( _
  byref aReference as ISomething )
  
  m_something = aReference
end constructor

destructor SomeObject()
end destructor
To be able to handle references right, FreeBasic would need to implement a garbage collector (like Java). Or simply allow late binding of references to interfaces.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: How to fix error 177 ???

Post by dodicat »

fxm
your example gives
Compiler output:
Aborting due to runtime error 12 ("segmentation violation" signal) in 32 bit gas and gcc
And compile failed with no reason shown in 64 bit gcc.

This also gives the same result

Code: Select all


type udt
    as integer n
    static byref null as udt
end type

dim byref as udt udt.null=*cptr(udt ptr,0)
sleep
  
Win 10 fb , version 1.05, fbide.
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: How to fix error 177 ???

Post by paul doe »

Munair wrote:Why would Null be part of the object?
Because not every object can be nullable, and the null pointer has to be of the same type as the object to avoid casting. The right way (depending on circumstances) would be to implement the Null Pattern for each object that needs it.

I would define the Null pointer as:

Code: Select all

#define Null cptr( any ptr, 0 )
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: How to fix error 177 ???

Post by paul doe »

Munair wrote:Why would Null be part of the object?
Sorry, I've written the code in a hurry. The Null reference should be part of the interface, not the derived classes themselves:

Code: Select all

/'
  The ID you'll use to identify objects throughout your
  code base.
'/
type ObjectID as string

/'
  The abstract interface for an inventory item
'/
type IInventory extends Object
  public:
    declare virtual destructor()
    declare virtual property ID() as ObjectID
    
    static Null as IInventory ptr
    
  protected:
    declare constructor()
    declare constructor( _
      byref as const ObjectID )
 
  private:
    m_ID as ObjectID
end type

dim as IInventory ptr _
  IInventory.Null = cptr( IInventory ptr, 0 )
  
constructor IInventory()
  this.constructor( "<Unknown>" )
end constructor

constructor IInventory( _
  byref anID as const ObjectID )
 
  m_ID = anID
end constructor

destructor IInventory()
end destructor

property IInventory.ID() as ObjectID
  return( m_ID )
end property

/'
  A concrete implementation of an inventory item
'/
type Asset extends IInventory
  public:
    declare constructor( _
      byref as const ObjectID )
    declare destructor() override
   
  private:
    declare constructor()
end type

constructor Asset()
  base()
end constructor

constructor Asset( _
  byref anID as const ObjectID )
 
  base( anID )
end constructor

destructor Asset()
end destructor

/'
  Finds an asset on a given array. Returns Asset.Null if
  it's not found.
'/
function findAsset( _
  assets() as Asset, _
  byref anID as const ObjectID ) as IInventory ptr
 
  for _
    i as integer = lbound( assets ) to ubound( assets )
   
    if( _
      assets( i ).ID = anID ) then
      return( @assets( i ) )
    end if
  next
 
  return( Asset.Null )
end function

/'
  Main code
'/
dim as Asset _
  someAssets( ... ) = _
  { _
    Asset( "Pears" ), _
    Asset( "Apples" ), _
    Asset( "Bananas" ) _
  }
/'
  See if we can find some asset
'/
var anAsset = findAsset( someAssets(), "Apples" )

if( _ '' Found it?
  anAsset <> IInventory.Null ) then
  /'
    Do some mighty interesting stuff
  '/
  ? "Found it!"
else
  '' Crap out
  ? "Couldn't find it!"
end if

sleep()
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: How to fix error 177 ???

Post by MrSwiss »

paul doe wrote:I would define the Null pointer as: #define Null cptr( any ptr, 0 )
Another way would be a (global) constant (not only good for OO stuff, but procedural also):
Const As Any Ptr NULL = 0
More efficient than a #Define (a single line #Macro).
Post Reply