I have been working on a few projects and needed a method similar to pythons string.split(). I decided to make a reusable piece of code and export it as a .dll, the code is below:

std::vector<std::string> Editor::StringHandler::Split(std::string data, std::string tokens)
    std::vector<std::string> returnList;
    std::string temp="";
    bool found=false;
    for (auto i : data)
    {

        found = false;
        for (auto token : tokens)
        {
            found = (i == token);
            if (found && temp.length() > 0)
            {
                returnList.push_back(temp);
                temp = "";
            }
            if (found)
            {
                break;
            }
        }

        if (!found)
        {
            temp += i;
        }

    }
    if (!found)
    {
        returnList.push_back(temp);
    }
    return returnList;
}

The code itself works fine if compiled into and executable and run; but if compiled into a dll and used in another project I get a run time error of access violation. I've tried using both returnList.reserve(data.length()); and temp.reserve(data.length()); but no avail. The call stack shows

>   msvcr110.dll!memcpy(unsigned char * dst, unsigned char * src, unsigned long count) Line 750 Unknown
    Render Engine V1.exe!wmain(int argc, wchar_t * * argv) Line 40  C++`
    Render Engine V1.exe!__tmainCRTStartup() Line 533   C`
    Render Engine V1.exe!wmainCRTStartup() Line 377 C

The code used to call the function is:

std::vector<std::string> vowels, novowels;
std::string info("test snippet of data");
std::string tokens("aeiou ");
vowels = Editor::StringHandler::Split(info);
novowels = Editor::StringHandler::Split(info, tokens);

for (auto s : vowels)
{
OutputLog(s.c_str());
}

std::cout << std::endl;

for (auto i : novowels)
{
OutputLog(i.c_str());
}

Any help as to why this is happening/how to fix it?

New memory allocated in a DLL must be deallocated in the DLL. You can't allocate memory in a DLL then deallocate it in the application program because the two have different memory heaps.

Here is another thread on that topic.

How did you export this function from your DLL?

You need to link both the DLL and the exe to the same instance of the run-time library.

Ie. link both with the shared version of the standard library: either /MDd (Debug) or /MD (Release)

I tried adding in a pointer to a string vector, std::vector<std::string>* buffer, in the parameter and replacing returnList.push_back(temp); with buffer->push_back(temp) but I still receive the unresolved externals error.

As for how I created it, I am using MS Visual Studio 2012 Express for Desktop. I created a DLL project and made sure that DATAEDITING_EXPORTS was defined for compiling. My header file is:

#ifdef DATAEDITING_EXPORTS
#define DATAEDITING_API __declspec(dllexport)
#else
#define DATAEDITING_API __declspec(dllimport)
#endif

#include <vector>
#include <string>

namespace Editor
{
    class StringHandler
    {
    public:
        static DATAEDITING_API void Split(std::vector<std::string>* buffer, std::string data);

        static DATAEDITING_API void Split(std::vector<std::string>* buffer, std::string data, std::string tokens);
    };
}

You are exporting your functions the wrong way!!!

Based on your code...

The code used to call the function is:
std::vector<std::string> vowels, novowels;
std::string info("test snippet of data");
std::string tokens("aeiou ");
vowels = Editor::StringHandler::Split(info);
novowels = Editor::StringHandler::Split(info, tokens);

...the functions Editor::StringHandler::Split() and its overload is expected to return a value which type is std::vector<std::string>. You defined these functions correctly but you exported them with type void.

To correctly export these functions use this syntax: std::vector<std::string> DATAEDITING_API Split(...)

And...just a humble comment: I don't see any benefit in creating dll exports that are static members of a class. Why don't just export these functions by themselves without a class and/or namespace?

@ vijayan121
I'm not sure what you mean, if you mean that I need to include the lib as well as having the DLL in the executables working directory than I've done that; using #pragma comment(lib, "Editor.lib") and moving the compiled files into the lib/ref/include dirs

@ RonalBertogi
That was my edited header file after following Ancient Dragon's advice. As for why, I would like to create a DLL with any misc code that I find myself reusing to keep things organised and compact.

It doesn't matter how you exported it or what lib you linked to since you can't create a vector in a dll and expect to destroy it in the application program because of heap differences. Won't work. You need another method in the DLL that will destroy the contents of the vector when no longer needed.

std::vector<std::string> Editor::StringHandler::Split(std::string data, std::string tokens)

You don't want to return a vector like that because it has to be duplicated when returned, which can be very very time consuming. Instead, pass a reference to the vector as a parameter so that Split can just modify it. Return either void or maybe bool to indicate success.

bool Editor::StringHandler::Split(std::vector<std::string> & returnList, std::string data, std::string tokens)

@Fearless Hornet,

I tried your code in my own project and it worked without errors. Of course, I did export your functions from my dll and called them in my exe.

However, did you try to execute your code step by step? I suggest you do this if you haven't yet and try to temporarily comment out the following lines:

for (auto s : vowels)
{
    OutputLog(s.c_str());
}
std::cout << std::endl;
for (auto i : novowels)
{
    OutputLog(i.c_str());
}

@vijayan121
The DLL was built with /MD and the project had /MT. Using /MD on the project initially gave errors but I was in debug mode, reverting to building both with /MDd made the program work, big thanks!

@Ancient Dragon
I already made that change since you first suggested it, thanks!

@RonalBertogi
I had the program printing each stage it went through (post-definitions, post calls to DLL, post output of data). I just thought it unnecessary to include it in the code snippet.

Thanks for the help all!

reverting to building both with /MDd made the program work

Building both with /MD would also work; other combinations would fail.

You don't want to return a vector like that because it has to be duplicated when returned, which can be very very time consuming

I do. Returning the vector by value is just as efficient and leads to cleaner code.
http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

It would seem that MS Visual Studio 2012 doesn't like building a debug release with /MD, only /MDd... Either way I have 2 DLL's one for debug (/MDd) and one for release (/MD)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.