Hello,

 I'd like to propose changes to the SAL_INFO etc. family of the new logging 
functions that would replace the somewhat strange usage

SAL_INFO("foo", "string " << s << " of length " << n)

 with

SAL_INFO("foo", "string %1 of length %2", s, n )

 while still leaving the possibility to do

SAL_INFO("foo", "string " + s + " of length "  + OUString::valueOf( n ))

 The last two are IMO much more natural than the iostream-based usage.

 The format-based usage uses a printf-like function that, unlike printf, is 
typesafe and extensible. If people would be interested, the function itself 
could be made public API (after all, there's a reason why printf is still 
popular even nowadays, despite all its shortcomings).

 Attached source code has a testing implementation of the format function and 
a simple logging macro. It still needs few final touches but it's generally 
ready. The templates may look scary at first, but it's rather simple, they 
are just making the function to take up to 9 arguments of each of the 
supported arguments and the template for return type is SFINAE[1]. I also had 
a look at the resulting code and it's usually pretty small (can be tweaked by 
where the inline keyword is put, this way the call at the place of usage is 
very small) and reasonably fast (could be done even faster if OUString being 
actually rather lame didn't make it somewhat pointless[2]).

 Now, onto the questions:

1) Yes/no ?
2) It take it SAL_INFO, being in sal, has to keep binary compatibility, which 
means we're stuck with the << usage if it stays that way in the 3.5 branch, 
and that I'd have to make this new way binary compatible in time for 3.5 if 
it's to replace it?
3) What would 'in time for 3.5' mean in practice?
4) What is the LO policy on char* <-> OUString conversions? It seems to me 
they always need to be explicit, but I'd prefer to ask.
5) For some of the features to work, it is necessary to build without 
gcc's -pedantic. I expect we already would generate some warnings if that was 
used anyway, wouldn't we? BTW, I've tried and built the code with GCC4.5 and 
MSVC 2008.

[1] http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error
[2] Today's trivia: Did you know that despite being seemingly highly 
optimized, with stuff like OUStringBuffer::makeStringAndClear() or the 
obnoxious RTL_CONSTASCII_USTRINGPARAM macro, O(U)String* actually does some 
rather pathetic things like ctors first checking if there's any memory to 
free or allocated memory being always zeroed first?

-- 
 Lubos Lunak
 l.lu...@suse.cz
// TODO all the OUStringBuffers appear to be rather inefficient, so either do something about them
// or don't bother about the performance of this code that much

#include <rtl/ustrbuf.hxx>
#include <string.h>

/**
 * Base class for formatting any type to a string. To add a new type, create a subclass
 * like in the following example.
 * @code
template<>
class Formatter< const sal_Char* > : public FormatterBase
{
public:
    Formatter( const sal_Char* arg ) : data( arg ) {}
    typedef const sal_Char* Type;
    virtual void append( rtl::OUStringBuffer& str ) const;
private:
    const sal_Char* const data;
};

void Formatter< const sal_Char* >::append( rtl::OUStringBuffer& str ) const
{
    str.appendAscii( data );
}
 * @endcode
 */
class FormatterBase
{
public:
    virtual void append( rtl::OUStringBuffer& str ) const = 0;
// TODO not really much point with OUStringBuffer being otherwise inefficient?    virtual int length() const = 0;
};

template< typename T > class Formatter {};

template<>
class Formatter< const sal_Char* > : public FormatterBase
{
public:
    Formatter( const sal_Char* arg ) : data( arg ) {}
    typedef const sal_Char* Type;
    virtual void append( rtl::OUStringBuffer& str ) const;
private:
    const sal_Char* const data;
};

void Formatter< const sal_Char* >::append( rtl::OUStringBuffer& str ) const
{
    str.appendAscii( data );
}

template< int N >
class Formatter< char[N] > : public Formatter< const sal_Char* >
{
public:
    Formatter( const sal_Char* arg ) : Formatter< const sal_Char* >( arg ) {}
};

template< int N >
class Formatter< const char[N] > : public Formatter< const sal_Char* >
{
public:
    Formatter( const sal_Char* arg ) : Formatter< const sal_Char* >( arg ) {}
};

// for types that work with OUStringBuffer::append(), 'cast' is a cast
// applied to the data when calling append() if needed
#define FORMATTER_APPEND_BASED( type, cast ) \
template<> \
class Formatter< type > : public FormatterBase \
{ \
public: \
    Formatter( const type& arg ) : data( arg ) {} \
    typedef type Type; \
    virtual void append( rtl::OUStringBuffer& str ) const; \
private: \
    const type& data; \
}; \
\
void Formatter< type >::append( rtl::OUStringBuffer& str ) const \
{ \
    str.append( cast data ); \
}

FORMATTER_APPEND_BASED( rtl::OUString, )
// MSVC requires the cast otherwise it's ambiguous (@%$@!! sal_Int types)
FORMATTER_APPEND_BASED( int, (sal_Int64) )
// there's no unsigned overload for OUStringBuffer::append(), so just cast it to sal_Int64
FORMATTER_APPEND_BASED( unsigned int, (sal_Int64) )
// there's no integer conversion when working with template arguments, so extra class for each int type is needed
FORMATTER_APPEND_BASED( short int, (sal_Int64) )
FORMATTER_APPEND_BASED( unsigned short int, (sal_Int64) )
FORMATTER_APPEND_BASED( long int, )
FORMATTER_APPEND_BASED( unsigned long int, (sal_Int64) )
// TODO these possibly lose significant digits
FORMATTER_APPEND_BASED( long long int, (sal_Int64) )
FORMATTER_APPEND_BASED( unsigned long long int, (sal_Int64) )
FORMATTER_APPEND_BASED( bool, (sal_Bool) )

//int Formatter< int >::length() const
//{ // cannot be exact, give maximum
//    return 40;
//}

rtl::OUString formatInternal( const rtl::OUString& str, const FormatterBase* f[], int fcount )
{
//    rtl::OUStringBuffer buffer( str.getLength() + f1.length() + f2.length() + f3.length());
    rtl::OUStringBuffer buffer;
    for( int pos = 0;
         pos < str.getLength();
         ++pos )
    {
        if( str[ pos ] == '%' && pos < str.getLength() - 1 )
        {
            ++pos;
            // TODO warn if no argument for format
            if( str[ pos ] >= '1' && str[ pos ] <= '0' + fcount )
                f[ str[ pos ] - '1' ]->append( buffer );
            else
                buffer.append( str[ pos ] );
        }
        else
            buffer.append( str[ pos ] );
    }
    return buffer.makeStringAndClear();
}

/**
 * The purpose of this template is only to ensure that format() is called only with arguments for which a matching Formatter
 * class exists, template instances with invalid arguments will not be created because of this class. The return type
 * of format() is actually rtl::OUString.
 */
template< typename T1, typename T2 = int, typename T3 = int, typename T4 = int, typename T5 = int, typename T6 = int,
    typename T7 = int, typename T8 = int, typename T9 = int >
struct FormatterTypesCheck
{
    typedef rtl::OUString Type;
};

/**
 * Create a new string based on the given format string with placeholders filled in with the given arguments.
 *
 * This is a printf-like function that is typesafe and extensible.
 */
template< typename T >
typename FormatterTypesCheck< typename Formatter< T >::Type >::Type
format( const rtl::OUString& str, const T& arg )
{
    Formatter< T > f( arg );
    const FormatterBase* fs[] = { &f };
    return formatInternal( str, fs, SAL_N_ELEMENTS( fs ));
}

/**
 * @overload
 */
template< typename T1, typename T2 >
typename FormatterTypesCheck< typename Formatter< T1 >::Type, typename Formatter< T2 >::Type >::Type
format( const rtl::OUString& str, const T1& arg1, const T2& arg2 )
{
    Formatter< T1 > f1( arg1 );
    Formatter< T2 > f2( arg2 );
    const FormatterBase* fs[] = { &f1, &f2 };
    return formatInternal( str, fs, SAL_N_ELEMENTS( fs ));
}

/**
 * @overload
 */
template< typename T1, typename T2, typename T3 >
typename FormatterTypesCheck< typename Formatter< T1 >::Type, typename Formatter< T2 >::Type, typename Formatter< T3 >::Type >::Type
format( const rtl::OUString& str, const T1& arg1, const T2& arg2, const T3& arg3 )
{
    Formatter< T1 > f1( arg1 );
    Formatter< T2 > f2( arg2 );
    Formatter< T3 > f3( arg3 );
    const FormatterBase* fs[] = { &f1, &f2, &f3 };
    return formatInternal( str, fs, SAL_N_ELEMENTS( fs ));
}

/**
 * @overload
 */
template< typename T1, typename T2, typename T3, typename T4 >
typename FormatterTypesCheck< typename Formatter< T1 >::Type, typename Formatter< T2 >::Type, typename Formatter< T3 >::Type,
    typename Formatter< T4 >::Type >::Type
format( const rtl::OUString& str, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4 )
{
    Formatter< T1 > f1( arg1 );
    Formatter< T2 > f2( arg2 );
    Formatter< T3 > f3( arg3 );
    Formatter< T4 > f4( arg4 );
    const FormatterBase* fs[] = { &f1, &f2, &f3, &f4 };
    return formatInternal( str, fs, SAL_N_ELEMENTS( fs ));
}

/**
 * @overload
 */
template< typename T1, typename T2, typename T3, typename T4, typename T5 >
typename FormatterTypesCheck< typename Formatter< T1 >::Type, typename Formatter< T2 >::Type, typename Formatter< T3 >::Type,
    typename Formatter< T4 >::Type, typename Formatter< T5 >::Type >::Type
format( const rtl::OUString& str, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5 )
{
    Formatter< T1 > f1( arg1 );
    Formatter< T2 > f2( arg2 );
    Formatter< T3 > f3( arg3 );
    Formatter< T4 > f4( arg4 );
    Formatter< T5 > f5( arg5 );
    const FormatterBase* fs[] = { &f1, &f2, &f3, &f4, &f5 };
    return formatInternal( str, fs, SAL_N_ELEMENTS( fs ));
}

/**
 * @overload
 */
template< typename T1, typename T2, typename T3, typename T4, typename T5, typename T6 >
typename FormatterTypesCheck< typename Formatter< T1 >::Type, typename Formatter< T2 >::Type, typename Formatter< T3 >::Type,
    typename Formatter< T4 >::Type, typename Formatter< T5 >::Type, typename Formatter< T6 >::Type >::Type
format( const rtl::OUString& str, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5,
    const T6& arg6 )
{
    Formatter< T1 > f1( arg1 );
    Formatter< T2 > f2( arg2 );
    Formatter< T3 > f3( arg3 );
    Formatter< T4 > f4( arg4 );
    Formatter< T5 > f5( arg5 );
    Formatter< T6 > f6( arg6 );
    const FormatterBase* fs[] = { &f1, &f2, &f3, &f4, &f5, &f6 };
    return formatInternal( str, fs, SAL_N_ELEMENTS( fs ));
}

/**
 * @overload
 */
template< typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7 >
typename FormatterTypesCheck< typename Formatter< T1 >::Type, typename Formatter< T2 >::Type, typename Formatter< T3 >::Type,
    typename Formatter< T4 >::Type, typename Formatter< T5 >::Type, typename Formatter< T6 >::Type,
    typename Formatter< T7 >::Type >::Type
format( const rtl::OUString& str, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5,
    const T6& arg6, const T7& arg7 )
{
    Formatter< T1 > f1( arg1 );
    Formatter< T2 > f2( arg2 );
    Formatter< T3 > f3( arg3 );
    Formatter< T4 > f4( arg4 );
    Formatter< T5 > f5( arg5 );
    Formatter< T6 > f6( arg6 );
    Formatter< T7 > f7( arg7 );
    const FormatterBase* fs[] = { &f1, &f2, &f3, &f4, &f5, &f6, &f7 };
    return formatInternal( str, fs, SAL_N_ELEMENTS( fs ));
}

/**
 * @overload
 */
template< typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8 >
typename FormatterTypesCheck< typename Formatter< T1 >::Type, typename Formatter< T2 >::Type, typename Formatter< T3 >::Type,
    typename Formatter< T4 >::Type, typename Formatter< T5 >::Type, typename Formatter< T6 >::Type,
    typename Formatter< T7 >::Type, typename Formatter< T8 >::Type >::Type
format( const rtl::OUString& str, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5,
    const T6& arg6, const T7& arg7, const T8& arg8 )
{
    Formatter< T1 > f1( arg1 );
    Formatter< T2 > f2( arg2 );
    Formatter< T3 > f3( arg3 );
    Formatter< T4 > f4( arg4 );
    Formatter< T5 > f5( arg5 );
    Formatter< T6 > f6( arg6 );
    Formatter< T7 > f7( arg7 );
    Formatter< T8 > f8( arg8 );
    const FormatterBase* fs[] = { &f1, &f2, &f3, &f4, &f5, &f6, &f7, &f8 };
    return formatInternal( str, fs, SAL_N_ELEMENTS( fs ));
}

/**
 * @overload
 */
template< typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9 >
typename FormatterTypesCheck< typename Formatter< T1 >::Type, typename Formatter< T2 >::Type, typename Formatter< T3 >::Type,
    typename Formatter< T4 >::Type, typename Formatter< T5 >::Type, typename Formatter< T6 >::Type,
    typename Formatter< T7 >::Type, typename Formatter< T8 >::Type, typename Formatter< T9 >::Type >::Type
format( const rtl::OUString& str, const T1& arg1, const T2& arg2, const T3& arg3, const T4& arg4, const T5& arg5,
    const T6& arg6, const T7& arg7, const T8& arg8, const T9& arg9 )
{
    Formatter< T1 > f1( arg1 );
    Formatter< T2 > f2( arg2 );
    Formatter< T3 > f3( arg3 );
    Formatter< T4 > f4( arg4 );
    Formatter< T5 > f5( arg5 );
    Formatter< T6 > f6( arg6 );
    Formatter< T7 > f7( arg7 );
    Formatter< T8 > f8( arg8 );
    Formatter< T9 > f9( arg9 );
    const FormatterBase* fs[] = { &f1, &f2, &f3, &f4, &f5, &f6, &f7, &f8, &f9 };
    return formatInternal( str, fs, SAL_N_ELEMENTS( fs ));
}

// testing functionality

using namespace rtl;
#include <stdio.h>

void formattest()
{
    OUString str1( RTL_CONSTASCII_USTRINGPARAM( "test %1 test" ));
    OUString data1( RTL_CONSTASCII_USTRINGPARAM( "foo" ));
    char char1[] = "char1";
    const char* const char2 = "char2";
    const char* char3 = "char3";
    const char char4[] = "char4";
    OUString res1 = format( str1, data1 );
    fprintf(stderr, "1: %s\n", rtl::OUStringToOString( res1, RTL_TEXTENCODING_UTF8 ).getStr());
    OUString res2 = format( str1, "foo" );
    fprintf(stderr, "2: %s\n", rtl::OUStringToOString( res2, RTL_TEXTENCODING_UTF8 ).getStr());
    OUString res3 = format( str1, (short)1 );
    fprintf(stderr, "3: %s\n", rtl::OUStringToOString( res3, RTL_TEXTENCODING_UTF8 ).getStr());
    // test all char variations
    format( str1, char1 );
    format( str1, char2 );
    format( str1, char3 );
    format( str1, char4 );
//    OUString res4 = format( str1, (void*)0 );
    OUString res5 = format( str1, true );
    fprintf(stderr, "5: %s\n", rtl::OUStringToOString( res5, RTL_TEXTENCODING_UTF8 ).getStr());
}

// testing the resulting assembler
OUString sizetest( const OUString& f, const OUString& str )
{
    return format( f, str );
}



// logging functionality based on format()

void logfunction( const char* area, const OUString& expression )
{
    fprintf( stderr, "LOG: %s: %s\n", area, rtl::OUStringToOString( expression, RTL_TEXTENCODING_UTF8 ).getStr());
}

inline
void logfunction( const char* area, const char* expression )
{
    fprintf( stderr, "LOG: %s: %s\n", area, expression );
}

template< typename T >
void logfunction( const char* area, const OUString& str, const T& arg )
{
    logfunction( area, format( str, arg ));
}

template< typename T1, typename T2 >
void logfunction( const char* area, const OUString& str, const T1& arg1, const T2& arg2 )
{
    logfunction( area, format( str, arg1, arg2 ));
}

template< typename T1, typename T2, typename T3 >
void logfunction( const char* area, const OUString& str, const T1& arg1, const T2& arg2, const T3& arg3 )
{
    logfunction( area, format( str, arg1, arg2, arg3 ));
}

template< typename T >
void logfunction( const char* area, const char* str, const T& arg )
{
// TODO avoid the OUString conversion?
    logfunction( area, format( OUString( str, strlen( str), RTL_TEXTENCODING_ASCII_US ), arg ));
}

template< typename T1, typename T2 >
void logfunction( const char* area, const char* str, const T1& arg1, const T2& arg2 )
{
    logfunction( area, format( OUString( str, strlen( str), RTL_TEXTENCODING_ASCII_US ), arg1, arg2 ));
}

template< typename T1, typename T2, typename T3 >
void logfunction( const char* area, const char* str, const T1& arg1, const T2& arg2, const T3& arg3 )
{
    logfunction( area, format( OUString( str, strlen( str), RTL_TEXTENCODING_ASCII_US ), arg1, arg2, arg3 ));
}


#define LOG( area, expression, ... ) \
    do \
    { \
        logfunction( area, expression ,##__VA_ARGS__ ); \
    } while( false ) \

void logtest()
{
    OUString str( RTL_CONSTASCII_USTRINGPARAM( "foo" ));
    OUString form( RTL_CONSTASCII_USTRINGPARAM( "test %1 test" ));
    LOG( "area1", "fast" );
    LOG( "area2", str );
    LOG( "area3", form, str );
    LOG( "area4", form, 1 );
//    LOG( "area5", form, (void*)0 );
    LOG( "area6", "test %1 this %2 test", str, false );
}

void logsizetest( const OUString& f, const OUString& str )
{
    LOG( "area1", f, str );
}

int main()
{
    formattest();
    logtest();
    return 0;
}
_______________________________________________
LibreOffice mailing list
LibreOffice@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/libreoffice

Reply via email to