tags:

views:

201

answers:

2

I have an nunit Test in C#, that calls a C# wrapper of a function in a C++ DLL. The C++ code uses std::cerr to output various messages.

These messages cannot be redirected using nunit-console /out /err or /xml switch. In nunit (the GUI version) the output does not appear anywhere.

I would like to be able to see this output in nunit (GUI version). Ideally I would like to be able to access this output in the Test.

Thanks for any help.

A: 

Redirecting std::cerr is a matter of replacing the stream buffer with your own. It is important to restore in original buffer before we exit. I don't know what your wrapper looks like, but you can probably figure out how to make it read output.str().

#include <iostream>
#include <sstream>
#include <cassert>

using namespace std;

int main()
{
    streambuf* buf(cerr.rdbuf());
    stringstream output;

    cerr.rdbuf(output.rdbuf());
    cerr << "Hello, world!" << endl;

    assert(output.str() == "Hello, world!\n");
    cerr.rdbuf(buf);

    return 0;
}
Eddy Pronk
See my other 'answer' for what I did with this.
Richard
A: 

Thanks for the hint. This is what I ended up doing:

.CPP file ------------------------

#include <iostream>
#include <sstream>

static std::stringstream buffer;
static std::streambuf * savedBuffer = NULL;


extern "C" __declspec(dllexport) bool Redirect()
{
    if (savedBuffer)
    {
        return false;
    }
    std::streambuf * buf(std::cerr.rdbuf());
    std::cerr.rdbuf(buffer.rdbuf());

    // This two lines are for illustration purposes only!
    std::cerr << "Hello world" << std::endl;

    return true;
}


extern "C" __declspec(dllexport) void Revert()
{
    if (savedBuffer)
    {
        std::cerr.rdbuf(savedBuffer);
    }
    savedBuffer = NULL;
}


extern "C" __declspec(dllexport) const char * getCerr()
{
    return _strdup(buffer.str().c_str());
}

extern "C" __declspec(dllexport) void freeCharPtr(char *ptr)
{
    free(ptr);
}

.CS file ------------------------------------------

public static class Redirector
{
    // PRIVATE ------------------------------------------------------------
    private const String LibraryName = "MyCpp.dll";

    [DllImport(LibraryName, CharSet = CharSet.Ansi)]
    private static extern IntPtr getCerr();

    // PUBLIC -------------------------------------------------------------
    [DllImport(LibraryName, CharSet = CharSet.Ansi)]
    public static extern bool Redirect();

    [DllImport(LibraryName, CharSet = CharSet.Ansi)]
    public static extern void Revert();

    [DllImport(LibraryName, CharSet = CharSet.Ansi)]
    internal static extern void freeCharPtr(IntPtr ptr);

    public static string GetCerr()
    {
        IntPtr temp = getCerr();
        string result = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(temp);
        freeCharPtr(temp);
        return result;
    }
}

NUnit Test -----------------------

    [Test]
    // [Ignore]
    public void TestRedirect()
    {
        Redirector.Redirect();
        // Call more functions that output to std::cerr here.
        Redirector.Revert();
        System.Console.WriteLine(Redirector.GetCerr());
    }

The freeCharPtr() stuff is necessary to free the allocated memory from _strdup(), since I could not work out (if it's even possible) how to marshal an std::string.

Note: This is not thread safe!

Richard