Open bitonic opened 9 years ago
Writing down those ideas would help others pick this up if they want to.
An idea for template support:
Anything with template polymorphism would be generated with an explicit function. This function would get a templated C++ implementation and a Haskell typeclass. You could then request specializations of this function for given Haskell types, which would produce typeclass instances.
Sketch:
data Vector a
[temp| {class PushBack a}
template <class $a>
void pushBack(const vector<$a> *foo), $a val) {
foo->push_back(val);
} |]
[tempInst| pushBack<int> |]
[tempInst| pushBack<char*> |]
... generates Haskell
class PushBack a where
pushBack :: Ptr (Vector a) -> a -> IO ()
foreign import ccall inline_c_pushBack_cInt ...
instance PushBack CInt where
pushBack = inline_c_pushBack_cInt
foreign import ccall inline_c_pushBack_cChar_ptr ...
instance PushBack (Ptr CChar) where
pushBack = inline_c_pushBack_cChar_ptr
and C++
template <class inline_c_a>
void pushBack(vector<inline_c_a> *foo), inline_c_a val) {
foo->push_back(val);
}
extern "C" {
static void inline_c_pushBack_cInt(vector<int> *foo, int val) {
pushBack<int>(foo, val);
}
}
etc..
Actually you could just use a regular block instead of a function, and you could share classes:
pushBack :: VectorImpl a => Ptr (Vector a) -> a -> IO ()
pushBack vec val =
[block| <class VectorImpl a> void {
$(vector<$type:a> *vec) ->push_back($($type:a val));
|]
resize :: VectorImpl a => Ptr (Vector a) -> Int -> IO ()
resize vec size =
[block| <class VectorImpl a> void {
$(vector<$type:a> *vec) ->resize($(int val));
|]
destruct :: VectorImpl a => Ptr (Vector a) -> IO ()
destruct vec =
[block| <class VectorImpl a> void {
delete $(vector<$type:a> *vec);
|]
[tempImpl| VectorImpl CInt |]
produces:
class VectorImpl a where
inline_c_pushBack_32193293 :: Ptr (Vector a) -> a -> IO ()
inline_c_resize_3232112131 :: Ptr (Vector a) -> Int -> IO ()
inline_c_destruct_43244234 :: Ptr (Vector a) -> IO ()
instance VectorImpl CInt where
...
Yes, that makes a lot of sense, thanks a lot for the input. I especially like the second mockup of how it would look like.
There is the slight disadvantage of an upfront setup, but I don't think we can do much better than that.
I fear that the hardest part of this will be parsing C++ types...
Some further thoughts:
It would be good if external modules could generate implementations of templating classes (eg. VectorImpl
above). This means they need access to the source of the templated C++ functions. It would be quite a pain to include the generated code from another module, so as a (slightly out there) alternative I propose that we encode the necessary information as an instance of a type family on these template typeclasses. This could then be recovered in TH in the external module and included with the template specializations.
data ClassImpl str = ClassImpl { templateSources :: [ ( str, str ) ] }
type family Template (cls :: k) :: ClassImpl Symbol
type instance Template VectorImpl
= 'ClassImpl
'[ '( "inline_c_pushBack_0", "template<class T>..." )
]
Or perhaps when we produce a templating class, eg. VectorImpl
, we could also produce a function createVectorImpl :: Name -> DecsQ
that could be called in other modules when specialization is required. Or have an intermediate step and have functions:
defVectorImpl :: Name -> TemplateClassInfo -- generated with VectorImpl
tempImpl :: TemplateClassInfo -> DecsQ -- in inline-c
and then it's tempImpl $ defVectorImpl ''CInt
at the use site.
The main things to do are:
I have some ideas for both, currently experimenting.