• Advertisement
Sign in to follow this  

Templates and DLLs

This topic is 3848 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I need to write a generic parsing engine (using boost::regex and boost::spirit) which resides in a DLL. I also need to pass a std::string and std::vector<std::vector<float>> as parameters. When I try to compile, I'm informed that templates cannot be used in functions declared as extern "C". Is there a way around this? I am using VS2005, will be calling it from a Borland C++ builder 2007 application. Language is C++ :) Cheers

Share this post


Link to post
Share on other sites
Advertisement
Avoid using templates with extern "C". You might be able to get away with writing wrapper classes that you use on both ends. You can probably cast through void * to get around this limitation, but that's dangerous.

There's probably a better way to do this that I don't know of, though. I'd only use those two suggestions as a last ditch effort.

Share this post


Link to post
Share on other sites
Be very careful with how you approach this. Since different DLLs can be linked to different runtimes, creating and releasing memory across these runtime boundaries is very dangerous if you are not careful. You also want to watch out for passing templates as arguments across boundaries as well. This, especially for std::string, is very dangerous because the implementation for the template on one compiler might be different than another. It is much safer to pass a const char * than the class itself.

Share this post


Link to post
Share on other sites
Quote:
Original post by Happy Noodle Boy
What error code did the compiler output? I've never heard of that issue before and would like to see what the docs say about it.


MS C2894

Share this post


Link to post
Share on other sites
Well I can change the std::string into a char* as Deception666 suggested. I also attempted to get around the vector one by doing the void* cast as well. This fixed those errors, but since I am using templates later on in the function, ie, the boost ones, the compiler balked.

Ra: how does one not use extern "c" when using dlls? I was under the impression this was put into place to stop name mangling which made it impossible to call dll functions?

Quote:
Since different DLLs can be linked to different runtimes, creating and releasing memory across these runtime boundaries is very dangerous if you are not careful.
The vector will be passed in by reference, so this should be fine correct? And all other instance variables created within the dll are scoped to the function's lifetime.

So how do I fix this then?

Share this post


Link to post
Share on other sites
Quote:
Original post by _Sigma
Well I can change the std::string into a char* as Deception666 suggested. I also attempted to get around the vector one by doing the void* cast as well. This fixed those errors, but since I am using templates later on in the function, ie, the boost ones, the compiler balked.


I would suggest you use a float* (along with a size) instead of attempting to pass the vector itself as a void* parameter, doing that can only lead to trouble when using different compilers. The data of a vector is guaranteed to be continuous in memory so you could just try:

my_dll_function(my_str.c_str(), &myvector[0], myvector.size());

Quote:

Ra: how does one not use extern "c" when using dlls? I was under the impression this was put into place to stop name mangling which made it impossible to call dll functions?


If you are using the same compiler, it is perfectly fine link C++ to C++ (with mangled names and all) even if part of the code is in another DLL.

Quote:

Quote:
Since different DLLs can be linked to different runtimes, creating and releasing memory across these runtime boundaries is very dangerous if you are not careful.
The vector will be passed in by reference, so this should be fine correct? And all other instance variables created within the dll are scoped to the function's lifetime.

So how do I fix this then?


You fix this by using basic data types, as parameters for your DLL exported functions. No classes, no templates, just regular C datatypes such as char* and float*.

Share this post


Link to post
Share on other sites
Quote:
Original post by wack
my_dll_function(my_str.c_str(), &myvector[0], myvector.size());


How do I access the vectors later in the fucnction? The vector being passed in is being modified in the function, so I need access to the push_back() method.

As well, lets say I did want to cast froma void*. ((std::vector<std::vector<float>>)array)->clear();. I'm told this is not a legal cast. I've tried boost lexical_cast as well as reinterpret_cast. Neither can do it...

Share this post


Link to post
Share on other sites
Quote:
Original post by _Sigma
Quote:
Original post by wack
my_dll_function(my_str.c_str(), &myvector[0], myvector.size());


How do I access the vectors later in the fucnction? The vector being passed in is being modified in the function, so I need access to the push_back() method.

As well, lets say I did want to cast froma void*. ((std::vector<std::vector<float>>)array)->clear();. I'm told this is not a legal cast. I've tried boost lexical_cast as well as reinterpret_cast. Neither can do it...


You just can't. Borlands stl has no knowledge about how Visual Studio's stl works. Even if you get it to compile, there will be horrible crashes waiting for you. You will just have to think of some other way than passing stl structures around.

Share this post


Link to post
Share on other sites
Quote:
Original post by Spoonbender
Or rethink the bit about putting this in a separate dll.

Well this was all to get around not being able to link to the boost libraries with my borland IDE. I have them (libs) compiled for vs 2005. So I suppose I will need to recompile for BCB, which I'd rather not do...

Share this post


Link to post
Share on other sites
Quote:
Original post by _Sigma
Quote:
Original post by Spoonbender
Or rethink the bit about putting this in a separate dll.

Well this was all to get around not being able to link to the boost libraries with my borland IDE. I have them (libs) compiled for vs 2005. So I suppose I will need to recompile for BCB, which I'd rather not do...


Unless you want some monstrosity with callback functions to manipulate stl data in the other environment, you don't have much choice.

Share this post


Link to post
Share on other sites
Damn - Regex is not supported for the new bcb... So much for standard conformance.

Share this post


Link to post
Share on other sites
There are some rules that you need to consider when using DLL's and templates (several of these have already been mentioned, but I've grouped them together):

1. A C++ template is a _compile_time_ abstraction which has to be resolved during compilation. If you want _run_time_ abstractions, you need to look at virtual base classes, but you probably know that.

2. Passing classes through DLL interfaces is only possible if their methods are exported by the dll. If they are derived from other classes, those need to be exported as well. Some libraries do export classes through a dll, but most often do it by using the virtual base class "pattern". (Xerces-C comes to mind)

3. Name mangling can become a problem if you export C++ decorated names and try to link with a dll built with a different compiler. That's why many DLL interfaces are built on a "standard C" API (or come with source). Of course, using "extern C" will force you "downgrade" to pure C datatypes.


Looking at your specific problem, you could perhaps solve it by making the DLL statefull in this way:

The vector is contained within the DLL and filled during parsing.
The DLL exposes methods to retrieve the size and contents of the vector, as well as some "clear" method. It could be done something like this:


void dll_function_1(const char* string_to_parse); // parses the string & fills the vector
unsigned dll_function_2(void); // returns the size of the vector
float* dll_function_3(void); // returns a pointer to the first element
void dll_function_4(void); // clears the vector

This is basically a way to expose (part of) the stl::vector interface so your BCB code can get at the data. The downside is that the dll will now contain state and is no longer thread-safe without additional work (i.e. thread-local storage etc), so this might not be an ideal solution based on your needs.

If you find yourself needing to do this for more parts of your API, have another look at your design to see if you perhaps need to move the DLL boundary to a higher hierarchy level in your application (i.e. have it do more work). Usually this will lead to an API that requires less communication, and less is more :-)

Share this post


Link to post
Share on other sites
Quote:
Original post by _Sigma
Damn - Regex is not supported for the new bcb... So much for standard conformance.


Back to square one then. I think your best option would be to make an interface that uses only basic data types. I'm not sure exactly what you're going to do, but how about exporting two functions?


//1
void my_function_i_wanted_to_export(const char* something, float** recv);

//2
void release_my_function_data(float* data);



And then let function 1 build an array of floats that it sets *recv to before returning. After the app is done with the float data (copied it into a real std::vector for further processing or something), it may call function 2 to release the data function 1 allocated.

How does that sound?

Share this post


Link to post
Share on other sites
Yeah, square one indeed.

El Greco: Thanks for the list. I knew some, but you cleared up some misconceptions/unknowns for me. Thanks.

Quote:
I'm not sure exactly what you're going to do
. I need to parse a tabular file with variable column counts. Ie. count be a 2x2 table, or 800x789. Size is known at run time.

I then need to find sums, averages, etc.

My initial idea was to read in a line, parse it using regex (I looked into boost::sprirt, but found it confusing, and I've used regex lots ) and populate the vector grid. But regex is not support for bcb.

My idea is as follows. I'm going to write a dll wrapper for boost::regex. Basically:

int parse(float* list, //memory allocated by the main application
char* line, //the line to parse
int col //total col count so we don't over write past array boundaries
)



I will have a 2D vector array in the main application, then read in and pass the DLL each line. Create a new std string from the char* to use with regex. Use regex to break it up, use lexical cast<float> to convert each matched string from string to float, and insert into the float array.

This way all the stl stuff can be kept in the main app and I'm not allocating any memory in the dll. After I build the vector line by line, I can do what I need with it.

Does this seem reasonable?

Share this post


Link to post
Share on other sites
Your idea sounds very reasonable (you probably have already implemented it by now). Your solution also works because the layout of the file you read is known at run-time.

A minor modification you could think about is something that happens often in the Win32 API:

  1. Call the function with an empty buffer to write to, and the function then only scans the input and returns the required buffer size.

  2. Then allocate the buffer and call the function again...


But I personally don't like this system, as the 'hard work' (the parsing) is done twice.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement