Giter Club home page Giter Club logo

Comments (22)

ftynse avatar ftynse commented on August 29, 2024

@TobiG I wrote it.

The problem with the userpointer is that isl may need to destroy it internally after the C++ id object goes out of scope. For example, an isl::id is created manually and used as a tuple id in a space. The space is used to construct the domain union_set, for which we compute the schedule. The C isl_id object survives throughout the process while the C++ isl::id may be long gone. So we want to store the user data in the C isl_id. The rest of the wrapper is necessary for two reasons:

  • allow capturing lambdas to be passed as deleters, which are expected to be pointers to C functions;
  • provide some amount of type-safety, in particular make sure the user-provided deleter takes an instance of the class stored in user pointer rather than void*, so we can safely pass a wrapped destructor.

My understanding that isl_id is essentially shared_ptr over the pair userpointer+name. It provides reference counting and supports a custom deleter exactly like shared_ptr, so I would expect it to behave similarly.

We considered an alternative encoding where user data is stored in a hashmap (isl::id)->(std::string, isl::data_wrapper_class) in the context to which the id belongs, but it (1) introduces more global-like state than I would like and (2) maintains user data alive longer then potentially necessary. It could be possible to put a dummy user object to the C isl_id and trigger the deletion of the user object from the C++ hash map, but this boils down to a more involved version of the implemented solution.

from isl.

ftynse avatar ftynse commented on August 29, 2024

The managed_isl is experimental and was not even tested with out stuff. I expect to make a local PR soon, and if it goes in, replicate it here.

from isl.

tobiasgrosser avatar tobiasgrosser commented on August 29, 2024

Interesting, I came up with the following "approach" for type safety:

template <typename T>                                                            
class managed_id : public isl::id {                                              
        const char *type_address;                                                
        static const char type_token;                                            
        T payload;                                                               
                                                                                 
public:                                                                          
        managed_id(isl::ctx ctx, std::string name, T payload)                    
                : isl::id(ctx, name, this), payload(payload) {                   
                type_address = &type_token;                                      
        }                                                                        
                                                                                 
        T get_payload() {                                                        
                managed_id *user = (managed_id *) get_user();                    
                ISLPP_ASSERT(user->type_address == type_address,                 
                             "Unexpected type identifier");                      
                return user->payload;                                            
        }                                                                        
};                                                                               
                                                                                 
template<typename T>                                                             
const char managed_id<T>::type_token = '\0';  

which is used as follows:

        isl::ctx ctx = isl_ctx_alloc();                                          
        managed_id<int> ID(ctx, "aa", S);                                        
                                                                                 
        std::string S2 = "hallo";                                                
        managed_id<std::string> ID2(ctx, "aa", S2);                              
                                                                                 
        printf("%d\n", ID.get_payload());                                        
        printf("%s\n", ID2.get_payload().c_str());                               

The idea is that we can use the address of a type specific static global to uniquely identify the type of the content and verify if the isl::id used to read the type is indeed of the same type as the isl::id used to store the type.

The above snippets are still broken as the isl pointer still references the isl id object, instead of an object that can be freed when the isl_id is destroyed. But the idea itself might be useful.

from isl.

tobiasgrosser avatar tobiasgrosser commented on August 29, 2024

@ftynse : Yes, it would be good to have a pull request to comment on.

from isl.

ftynse avatar ftynse commented on August 29, 2024

@TobiG cool trick. I think it can be combined rather easily with what we have.

I did not want to have a class template for the id type itself because it could prevent me from having an (stl-style) list of ids, which expects instances of the same class. Particularly, the size of managed_id depends on the size of its template argument. List of pointers + inheritance would solve the problem, but it seemed inconsistent with the no-pointer-and-use-copy-everywhere semantics of the other parts. Also, isl_id being essentially a shared_ptr, taking a pointer to a shared pointer looks weird.

We can hide this under the template function get<T>, and additionally introduce try_get<T> / get_or_null<T> and isa<T> for probing.

from isl.

tobiasgrosser avatar tobiasgrosser commented on August 29, 2024

Sounds great. Yes, I think the two ideas are quite complementary and can easily be combined. I like your non-templated class as this also avoids the need to return an isl_id in get_id() functions and later cast it to something else.

How did you generate this class. Do you have patches to the isl C++ generator?

from isl.

ftynse avatar ftynse commented on August 29, 2024

So far it is hand written in isl.h.top. Not sure whether we want to patch the generator instead. My understanding is that we don't necessarily want the generator to have a large block of code it just prints instead of having the code directly.

from isl.

tobiasgrosser avatar tobiasgrosser commented on August 29, 2024

I was mostly interested how you did it. I think starting off from a class that is defined in isl.h.top is a good idea. We can then still see if we want to automate some of the generation. It might indeed not be needed.

from isl.

ftynse avatar ftynse commented on August 29, 2024

More discussion:
calls to isl_id_alloc with the same arguments give you same (pointer-comparable) ids. If we want to preserve the same behavior in C++ with an additional wrapper, we will have to maintain a map "user pointer -> wrapped user pointer" on the C++ side ourselves. This is tricky in a header-only interface...

from isl.

tobiasgrosser avatar tobiasgrosser commented on August 29, 2024

I don't understand. Do you want that the pointer addresses of two isl::ids compare equal if their underlying isl_ids compare equal. Or do you want them to compare equal for std::set and Co. I think in this case, we should overload std::less or operator<, no?

from isl.

ftynse avatar ftynse commented on August 29, 2024
isl_ctx *ctx = isl_ctx_alloc();
int i = 42;
isl_id *id1 = isl_id_alloc(ctx, "name", &i);
isl_id *id2 = isl_id_alloc(ctx, "name", &i);
assert(id1 == id2); // this holds!

it would make sense to have the same behavior in C++

isl::ctx ctx(isl_ctx_alloc());
int i = 42;
isl::id id1(ctx, "name", &i);
isl::id id2(ctx, "name", &i);
assert(id1 == id2); // expect to hold
assert(id1.get() == id2.get()); // isl should also know that these ids are the same.

Because we do not put a raw pointer to a C++ object, but a wrapper we constructed internally, isl will be unable to match user pointers. We have to keep track of the internally-constructed wrapper objects.

from isl.

tobiasgrosser avatar tobiasgrosser commented on August 29, 2024

I see. In this case, I don't think we can reasonably piggy-pack anything to the user pointer. I also don't believe that keeping a separate map in the C++ interface is the right solution. Instead, I would likely add an additional field to isl's isl_id struct and add an interface that allows the C++ bindings to officially store the data it needs.

from isl.

ftynse avatar ftynse commented on August 29, 2024

I don't see how an extra pointer would help. My user pointer-packing magic is intended to accept std::function as a deleter instead of a C function pointer. We cannot decay an std::function to a function pointer, so we store it instead and provide a deleter function that recovers the std::function and calls it.

An alternative would be keep C function pointer as a deleter in C++ interface. At this point, the id can be treated just like any other class.

from isl.

tobiasgrosser avatar tobiasgrosser commented on August 29, 2024

You could do your very same packing and pass the pointer you pass today in the extra pointer and the original pointer as key in today's pointer field, could you?

from isl.

tobiasgrosser avatar tobiasgrosser commented on August 29, 2024

Regarding the alternative. I think it might make sense to start with the most simple solution that matches the C interface as closely as possible. Could we just export isl_id with __isl_export and then add the missing functionality to the generator to generate code for it as it is (or write the code that would be generated by hand). In the end we want the C and C++ interface to be reasonably similar.

from isl.

ftynse avatar ftynse commented on August 29, 2024

Well, the easiest solution would be to force the deleter in the C++ interface to be a C function pointer.

from isl.

ftynse avatar ftynse commented on August 29, 2024

Otherwise, having two user pointers in the C isl_id seems weird...
However, we can try to get around that by arguing that usr1 is included in the identity of isl_id and usr2 is not. So isl_ids with equal usr2 pointers are not guaranteed to be equal.

from isl.

ftynse avatar ftynse commented on August 29, 2024

But I still don't see how that would help. It would either break pointer-comparability of ids in C, or require the second pointers to be the same to get you the same id object, which brings us back to the original problem: maintain a mapping between actual user pointers aka usr1 and extra data created for that pointer aka usr2 in order to create identical pointers.

from isl.

ftynse avatar ftynse commented on August 29, 2024

@TobiG @nicolasvasilache
So here are my two propositions https://github.com/nicolasvasilache/isl/pull/17 https://github.com/nicolasvasilache/isl/pull/18.

We may want to keep https://github.com/nicolasvasilache/isl/pull/18 and propose https://github.com/nicolasvasilache/isl/pull/17 in a documentation as a way for the user of islpp library to have arbitrarily-typed deleters and preserve the isl_id pointer-comparability.

from isl.

nicolasvasilache avatar nicolasvasilache commented on August 29, 2024

from isl.

tobiasgrosser avatar tobiasgrosser commented on August 29, 2024

@ftynse , I also believe the lightweight version is preferable for now. Would you be interested in preparing a patch that is upstreamable? It seems the code is very close already.

I would suggest to auto-generate the code, as this will make the patch a lot smaller and would raise less concerns during a patch review.

You can likely just:

  1. export isl_id
  2. teach the types about void_ptr
  3. Fix the printing of the callback setter

I would probably start without additional comparison operators or constructors and would for now also not move the deleter into the constructor. I would possibly even not template the code for now.

While this removes some of the features we (mostly you) came up with, it is likely a very good start and very uncontroversial.

from isl.

tobiasgrosser avatar tobiasgrosser commented on August 29, 2024

For #11 having isl_id exported would be useful.

from isl.

Related Issues (12)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.