#check if two objects are the same object

1 messages · Page 1 of 1 (latest)

ancient sentinel
#
src/tracer.zig:435:85: error: incompatible types: '?tracer.Object' and 'tracer.Sphere'
                            if (shadowClosestObject != null and shadowClosestObject != notTriangle) {
                                                                ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~

How can I check if two objects are the same object as in have the same spot in memory. And will checking if they have the same spot in memory even work in Zig becuase the compiler may or may not clone an object passed into a function depending on whether it thinks it'll be more efficient or not.

cedar spindle
#

if you have two pointers, compare them.
if you have two values, they are the same object if they are the same variable

ancient sentinel
#

so

const a = Object{};
const b = a;
a == b // returns false
#

?

stark haven
#

no that should be true

cedar spindle
#

a and b obviously don't live in the same memory location since they are different variables.
they do however contain the same bit pattern.

stark haven
celest charm
#

there are no "objects" in zig, so the very premise of the question is flawed

ancient sentinel
#

should I say "instances of structs"?

celest charm
#

I mean sure, but the problem isn't the term, it's the semantics

stark haven
#

sure, but now the issue is you have structs of 2 different types

celest charm
#

object equality makes sense because objects are secrelty just pointers

stark haven
#

it seems you've built some kind of dynamic dispatch system? if so, the way to do this check will depend on how that works

celest charm
#

you can "copy" around an object or a pointer, and the pointer continues to point to the same thing, thus it's "equal" to itself

#

however the equality between two sets of bytes (ie an instance of a struct) is more complicated and arbitrary

ancient sentinel
#

someone previously told me to not pass things around by pointer which is what I would be doing if I was programming in C

celest charm
#

by itself that seems like not very useful advice

#

there are many situations where you have to take things by pointer

ancient sentinel
#

is taking things by pointer more efficient?

celest charm
#

it can often be, yes, though it depends on what we're talking about

#

taking a u8 by pointer would probably be less efficient than just copying it

#

same for u16, u32, and then depending on the platform u64

cedar spindle
#

(ofc if the pointer is mutable you get different semantics; you can rewrite its value)

celest charm
#

often the compiler should be able to do the optimisation to convert values to pointers, but if the performance is important, it can pay to pay attention

ancient sentinel
#

so when I do

switch (object) {
            .sphere => |mySphere| {
                signedDist = sphereSDF(&ray, mySphere);
            },

mySphere is a clone of the sphere, but doing |*mySphere| would be a pointer to the sphere

ancient sentinel
cedar spindle
#

use the address-of operator (&) with a variable (declared with var):

var a: u64 = 42;
const pa: *u64 = &a;
pa.* += 1;
std.debug.assert(a == 43);
#

(the pointer itself need not be declared with var, only the data it points to)

ancient sentinel
#

so I have to dereference a pointer to make it mutable?

#

because I have

fn sphereSDF(ray: *Ray, mySphere: *const Sphere) ?f32 {
  // ...
}

pub fn rayTrace(scene_: *ObjectArray(Object), ray: *Ray, bounces: u32) {
  const signedDist = sphereSDF(ray, Sphere{});
}
var myRay = Tracer.Ray{
    .originX = xPos,
    .originY = yPos,        
    
    // the ray's x, y, z positions
    .pos = Vector.vec3(f32, xVel, yVel, 1),

    // the ray's x, y, and z velocities
    .xVel = xVel,
    .yVel = yVel,
    .zVel = 1,

    // stores whether the ray has hit something
    .hit = false,

    .isShadow = false,

    // stores the color the ray hit
    .clr = [3]u8{0, 0, 0}
};

const clr = Tracer.rayTrace(&scene, &myRay, 0).clr;

and am getting

src/tracer.zig:354:40: error: expected type '*tracer.Ray', found '*const *tracer.Ray'
                signedDist = sphereSDF(&ray, mySphere);
cedar spindle
#

you're taking a pointer to a temporary value of type *tracer.Ray. pointers to temporaries produce const pointers (*const a for some a).
remove the addr-of operator

ancient sentinel
ancient sentinel
cedar spindle
#

a const pointer means that the data it points to cannot be modified.
if the variable holding the pointer itself is declared var, it may be changed to point to other data.

ancient sentinel
#

ok, but I want to modify the data it is pointing to

cedar spindle
#

use a mutable pointer

ancient sentinel
#

because the default is const

hazy burrow
#

in zig default is mut

cedar spindle
#

what you know as &mut T in Rust is *T in Zig. &T in Rust is *const T in Zig

#

(up to Rust lifetimes and such)

ancient sentinel
hazy burrow
#

no it's not

cedar spindle
#
pub fn main() !void {
    const Ray = struct {};
    var thing = Ray{};
    var my_ptr = &thing;
    my_ptr = my_ptr; // supress error "variable never mutatedd"
    std.debug.print("{}\n", .{@TypeOf(my_ptr)});
}

console output:

*main.main.Ray
#

it's mutable.

hazy burrow
#

yep

#

ooh I like your mutation supression lol

#

kind of more fun than mine

ancient sentinel
#

then why am I getting
error: expected type '*tracer.Ray', found '*const *tracer.Ray'

cedar spindle
hazy burrow
#

you have a second addrof somewhere by accident

#

I would assume that if it's this thing:

src/tracer.zig:354:40: error: expected type '*tracer.Ray', found '*const *tracer.Ray'
                signedDist = sphereSDF(&ray, mySphere);
#

that just sphereSDF(ray, mySphere) works

#

pub fn rayTrace(scene_: *ObjectArray(Object), ray: *Ray, bounces: u32) {

#

ray is already a *Ray

#

doing &ray will get you a pointer to the pointer

#

therefore *const *tracer.Ray

ancient sentinel
#

so what does derefercing a pointer do? Because apparently I can modify the data the pointer points to without dereferencing it

hazy burrow
#

. does one layer of automatic dereferencing

#

so .x on a pointer is equivalent to .*.x

#

that's not control flow

#

so no

ancient sentinel
#

i'm dum

hazy burrow
#

but there's lots of hidden control flow in zig

#

lots of calls to memcpy

#

and other compiler_rt functions

ancient sentinel
#

Is doing stuff like this safe or is it undefined behavior?

const std = @import("std");

const Thing = struct {
    x: i32
};

fn make() *Thing {
    var abc = Thing{ .x = 1 };
    return &abc;
}

pub fn main() void {
    const myPtr = make();
    myPtr.x += 1;
    std.debug.print("{}", .{myPtr.*});
}
hazy burrow
#

latter

cedar spindle
hazy burrow
#

it's not a leak, it's a dangling pointer

#

but everything else yes

ancient sentinel
#

it'd be helpful if the compiler threw an error rather than it functioning as I would expect

cedar spindle
cedar spindle
hazy burrow
#

it's like halting problem hard

cedar spindle
#

makes me think...

ancient sentinel
#

it'd probably also make the zig compiler as slow as Rust's

stark haven
cedar spindle
stark haven
#

no, sorry, i should have said "very difficult" or something

hazy burrow
#

because that makes people think that the compiler will catch that for you

#

and lull you into a false sense of security

hazy burrow
cedar spindle
hazy burrow
#

which is proven to be impossible

cedar spindle
#

the annotations simply make the analysis intra-procedural

cedar spindle
#

Idonno - I'm a nerd, I read academic papers about programming languages - maybe others do too ¯_(ツ)_/¯

hazy burrow
#

I'm more of an implementation kind of guy

#

I spend most of my time awake staring at machine code

cedar spindle
#

the basic idea of memory regions and region subtyping fits Zig like a glove
this could actually be a reasonable thing to do...

ancient sentinel
celest charm
#

&shadowClosestObject.? != &notTriable will always be true

#

there is no circumstance under which pointers to two different local variables will be equal (ignoring undefined behaviour)

#

what are you trying to do?

#

are you trying to check whether they contain equal field values or something?

ancient sentinel
celest charm
#

well yeah

#

pointers are themselves a kind of value

#

you can compare them

ancient sentinel
#

but if I convert the ptrs to int then it'll check if they point to the same value which is what I'm trying to do

celest charm
#

no

#

that will just compare the integers

#

when you compile down code, pointers are simply integers

#

they are like an index into ram

ancient sentinel
#

and the integers are locations in memory and if both pointers point to the same location in memory than they are pointing to the same "object"

celest charm
#

if calling it object helps your mental model, yeah

#

so comparing integers is like comparing indexes into an array

ancient sentinel
#

converting ptr to int seems to work, but is there better way to do it?

hazy burrow
#

show full code

celest charm
#

so wait, just to be clear, you are trying to check if they point to the same thing?

#

if so, then the converting to integers thing is the way to do it

#

but to be clear, &x == &y will always be false

ancient sentinel
#

yes I'm trying to check if two variables are pointing to the same thing.

#

thanks

celest charm
#

two variables cannot point to the same thing

#

it will always be false

ancient sentinel
#

I mean the pointers that the variables store

celest charm
#

?

stark haven
#

if x and y are different variables, they live at different locations in memory, so pointers to them will not be equal

stark haven
celest charm
#

they are bytes that live on the stack

ancient sentinel
#

I think we're using the same terminology to mean different things

celest charm
#

if that were true you would understand why &x == &y makes no sense

ancient sentinel
#

I now understand why &x == &y makes no sense, but I didn't realize it before you just pointed it out

celest charm
#

ah alright