printf-like function for C#









up vote
-2
down vote

favorite
2












I want to write function for C#, which will be similar to sprintf(3) and vsnprintf(3) functions in C (or similar to "format" command in Tcl). Currently I have some success with it, but still have a few questions.



Below is the source code. Class "Format" contains few public functions, which accepts variable arguments list (but no more than 7 arguments, see below) and produce formatted string. This is analog of sprintf(3) function. Also function "format" may accept C# tuple, which should hold format string in first element, and arguments for format string in other elements -- this is analog of vsprintf(3) function.



Unfortunately, here I have two major limitations:



1) I can't pass more than seven arguments to format() function, because arguments passed in C# tuple, and tuple can't have more than eight elements (first element is format string itself, this is needed because empty tuples unavailable in C#). I hope to obtain some suggestions, how can I improve my format() function to avoid this limitation.



2) Another major limitation, that not all types can be passed to C# template. Especially, I can't pass pointers to format function (in this case I will got following error: "error CS0306: The type 'int*' may not be used as a type argument"). This is C# limitation? It's possible to rewrite format() function(s) to avoid this limitation?



Another major inconvenience, is that I should open some particular library name, and use C function name, which is different for different operation systems:



  • for windows I should use "msvcrt.dll" and "_snprintf";


  • for linux I should use "libc.so.6" and "snprintf";


  • for linux on embedded platform C-library name might have different name...


It's possible to define which library should be opened at runtime, or extern functions marshalling might be determined only at compile time? So, I see two variants possible here:



1) I need produce different DLL's for different target platforms;



2) Or I can decide at runtime, which function name and libc-library should be used?



I not understood, how can I rewrite the code for first or second variant. Also it looks very inconvenient to have knowledge about libc name. Why just not call dlsym("snprintf") ?



Another question, I trying limit using of dynamic memory, but looks It's impossible to avoid allocating few String and StringBuilder classes on heap. This is looking scary, because when programming on C/C++ it's possible to do almost all work without dynamic memory allocation. May be somebody suggests me, how to improve format() functions to avoid dynamic memory allocation.



using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace FK0

using Args = ITuple;

public class Format

// const string LIBC = "msvcrt.dll"; //"libc.so";
// const string FUNC = "_snprintf";
const string LIBC = "libc.so.6";
const string FUNC = "snprintf";
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.I4)] int a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.I8)] long a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, double a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.LPStr)]string a1);

// function returns length of next format segment (string, copied as is, or single format spec.)
static private int parse_format(string fmt, int pos)

int p = fmt.IndexOf('%', pos);
if (p == -1) return fmt.Length - pos; // copy to end of string
else if (p != pos) return p - pos; // copy till %

char fmt_term = 'd','i','o','u','x','X','e','E','f','F','g','G','a','A','c','s','p','n','%' ;
int e = fmt.IndexOfAny(fmt_term, p + 1);
if (e == -1) throw new System.ArgumentException("invalid format string");
return e - p + 1; // format specifier length


// call real `snprintf(3)' from C-library, marshal arguments appropriately
static private int call_snprintf(ref StringBuilder res, int len, StringBuilder fmt, Object arg)
arg is IntPtr) // XXX can't pass pointer to template!!!
return snprintf(res, (IntPtr)len, fmt, ((IntPtr)arg).ToInt64());
//else if (arg.GetType()
else
return snprintf(res, (IntPtr)len, fmt, Convert.ToInt32(arg));


// vsnprintf-like function (accepts all arguments in tuple)
static public string format(Args args)

if (! (args[0] is string)) // check, that first argument is string
throw new System.ArgumentException("wrong string format type");

// first pass
// compute number of arguments, size of output string and max size of formatted output
string fmt = args[0].ToString();
StringBuilder ns = null, fs = new StringBuilder();
int total_len = 0, maxlen = 0, narg = 1;
int pos = 0;
while (pos < fmt.Length)
int len = parse_format(fmt, pos);
if (fmt[pos] == '%') // pass format specifier to snprintf(3)
fs.Clear(); fs.Append(fmt, pos, len);
int flen = call_snprintf(ref ns, 0, fs, args[narg]);
if (flen == -1) throw new System.ArgumentException("wrong format string");
total_len += flen;
if (flen > maxlen) maxlen = flen;
narg++;

else // compute size of literal part
total_len += len;

pos += len;


if (narg != args.Length)
throw new System.ArgumentException("incorrect # of arguments for format string");

// second pass
// print each argument separately
var result = new StringBuilder(total_len);
var part = new StringBuilder(maxlen + 1); // include terminating zero
pos = 0; narg = 1;
while (pos < fmt.Length)
int len = parse_format(fmt, pos);
if (fmt[pos] == '%') // pass format specifier to snprintf(3)
fs.Clear(); fs.Append(fmt, pos, len);
call_snprintf(ref part, part.Capacity, fs, args[narg++]);
result.Append(part);
Console.WriteLine(part);

else // copy literal part as is
result.Append(fmt, pos, len);

pos += len;


return result.ToString();


// C# have no vararg templates, as C++03, also max size of tuple limited to 8 elements,
// also impossible to create empty tuple, so maximum number arguments limited to 7 (plus format string as 0-th element).

static public string format<T1, T2, T3, T4, T5, T6, T7>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5, a6, a7));


static public string format<T1, T2, T3, T4, T5, T6>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5, a6));


static public string format<T1, T2, T3, T4, T5>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5));


static public string format<T1, T2, T3, T4>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4)

return format(Tuple.Create(fmt, a1, a2, a3, a4));


static public string format<T1, T2, T3>(string fmt, T1 a1, T2 a2, T3 a3)

return format(Tuple.Create(fmt, a1, a2, a3));


static public string format<T1, T2>(string fmt, T1 a1, T2 a2)

return format(Tuple.Create(fmt, a1, a2));


static public string format<T1>(string fmt, T1 a1)

return format(Tuple.Create(fmt, a1));


static public string format(string fmt)

return format(Tuple.Create(fmt));

;


public class Program

unsafe public static void Main()

// System.Threading.Thread.Sleep(100000);
int z = 123;
int* y = &z;
IntPtr v = (IntPtr)y;
string s = FK0.Format.format("%p %d %d", v, *y, z);
Console.WriteLine(s);











share|improve this question

















  • 4




    Is there a particular reason you want to use this rather than the existing C# capabilities like string.Format?
    – mjwills
    Nov 9 at 11:49










  • Similar question: stackoverflow.com/questions/28155317/…
    – fk0
    Nov 9 at 11:58










  • Besides this huge amount of work that could be easily done with a string.Format call as @mjwills said, you can always create a public class Tuple<T1, T2, ....., T25> if you want. It won't solve the problem, though
    – Camilo Terevinto
    Nov 9 at 11:58






  • 1




    There's also a (undocumented?) keyword called __arglist which can be used to call varargs functions like printf. But I would recommend sticking to String.Format for code that's in a production environment.
    – Dirk
    Nov 9 at 12:04







  • 1




    There are like half a dozen questions in here, and the entire enterprise seems like a bad idea in the first place; if you want to call printf, just call the C++/CLI version of printf! Can you break this out into one simple question per question where each question has a clear, specific answer?
    – Eric Lippert
    Nov 9 at 21:49














up vote
-2
down vote

favorite
2












I want to write function for C#, which will be similar to sprintf(3) and vsnprintf(3) functions in C (or similar to "format" command in Tcl). Currently I have some success with it, but still have a few questions.



Below is the source code. Class "Format" contains few public functions, which accepts variable arguments list (but no more than 7 arguments, see below) and produce formatted string. This is analog of sprintf(3) function. Also function "format" may accept C# tuple, which should hold format string in first element, and arguments for format string in other elements -- this is analog of vsprintf(3) function.



Unfortunately, here I have two major limitations:



1) I can't pass more than seven arguments to format() function, because arguments passed in C# tuple, and tuple can't have more than eight elements (first element is format string itself, this is needed because empty tuples unavailable in C#). I hope to obtain some suggestions, how can I improve my format() function to avoid this limitation.



2) Another major limitation, that not all types can be passed to C# template. Especially, I can't pass pointers to format function (in this case I will got following error: "error CS0306: The type 'int*' may not be used as a type argument"). This is C# limitation? It's possible to rewrite format() function(s) to avoid this limitation?



Another major inconvenience, is that I should open some particular library name, and use C function name, which is different for different operation systems:



  • for windows I should use "msvcrt.dll" and "_snprintf";


  • for linux I should use "libc.so.6" and "snprintf";


  • for linux on embedded platform C-library name might have different name...


It's possible to define which library should be opened at runtime, or extern functions marshalling might be determined only at compile time? So, I see two variants possible here:



1) I need produce different DLL's for different target platforms;



2) Or I can decide at runtime, which function name and libc-library should be used?



I not understood, how can I rewrite the code for first or second variant. Also it looks very inconvenient to have knowledge about libc name. Why just not call dlsym("snprintf") ?



Another question, I trying limit using of dynamic memory, but looks It's impossible to avoid allocating few String and StringBuilder classes on heap. This is looking scary, because when programming on C/C++ it's possible to do almost all work without dynamic memory allocation. May be somebody suggests me, how to improve format() functions to avoid dynamic memory allocation.



using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace FK0

using Args = ITuple;

public class Format

// const string LIBC = "msvcrt.dll"; //"libc.so";
// const string FUNC = "_snprintf";
const string LIBC = "libc.so.6";
const string FUNC = "snprintf";
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.I4)] int a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.I8)] long a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, double a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.LPStr)]string a1);

// function returns length of next format segment (string, copied as is, or single format spec.)
static private int parse_format(string fmt, int pos)

int p = fmt.IndexOf('%', pos);
if (p == -1) return fmt.Length - pos; // copy to end of string
else if (p != pos) return p - pos; // copy till %

char fmt_term = 'd','i','o','u','x','X','e','E','f','F','g','G','a','A','c','s','p','n','%' ;
int e = fmt.IndexOfAny(fmt_term, p + 1);
if (e == -1) throw new System.ArgumentException("invalid format string");
return e - p + 1; // format specifier length


// call real `snprintf(3)' from C-library, marshal arguments appropriately
static private int call_snprintf(ref StringBuilder res, int len, StringBuilder fmt, Object arg)
arg is IntPtr) // XXX can't pass pointer to template!!!
return snprintf(res, (IntPtr)len, fmt, ((IntPtr)arg).ToInt64());
//else if (arg.GetType()
else
return snprintf(res, (IntPtr)len, fmt, Convert.ToInt32(arg));


// vsnprintf-like function (accepts all arguments in tuple)
static public string format(Args args)

if (! (args[0] is string)) // check, that first argument is string
throw new System.ArgumentException("wrong string format type");

// first pass
// compute number of arguments, size of output string and max size of formatted output
string fmt = args[0].ToString();
StringBuilder ns = null, fs = new StringBuilder();
int total_len = 0, maxlen = 0, narg = 1;
int pos = 0;
while (pos < fmt.Length)
int len = parse_format(fmt, pos);
if (fmt[pos] == '%') // pass format specifier to snprintf(3)
fs.Clear(); fs.Append(fmt, pos, len);
int flen = call_snprintf(ref ns, 0, fs, args[narg]);
if (flen == -1) throw new System.ArgumentException("wrong format string");
total_len += flen;
if (flen > maxlen) maxlen = flen;
narg++;

else // compute size of literal part
total_len += len;

pos += len;


if (narg != args.Length)
throw new System.ArgumentException("incorrect # of arguments for format string");

// second pass
// print each argument separately
var result = new StringBuilder(total_len);
var part = new StringBuilder(maxlen + 1); // include terminating zero
pos = 0; narg = 1;
while (pos < fmt.Length)
int len = parse_format(fmt, pos);
if (fmt[pos] == '%') // pass format specifier to snprintf(3)
fs.Clear(); fs.Append(fmt, pos, len);
call_snprintf(ref part, part.Capacity, fs, args[narg++]);
result.Append(part);
Console.WriteLine(part);

else // copy literal part as is
result.Append(fmt, pos, len);

pos += len;


return result.ToString();


// C# have no vararg templates, as C++03, also max size of tuple limited to 8 elements,
// also impossible to create empty tuple, so maximum number arguments limited to 7 (plus format string as 0-th element).

static public string format<T1, T2, T3, T4, T5, T6, T7>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5, a6, a7));


static public string format<T1, T2, T3, T4, T5, T6>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5, a6));


static public string format<T1, T2, T3, T4, T5>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5));


static public string format<T1, T2, T3, T4>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4)

return format(Tuple.Create(fmt, a1, a2, a3, a4));


static public string format<T1, T2, T3>(string fmt, T1 a1, T2 a2, T3 a3)

return format(Tuple.Create(fmt, a1, a2, a3));


static public string format<T1, T2>(string fmt, T1 a1, T2 a2)

return format(Tuple.Create(fmt, a1, a2));


static public string format<T1>(string fmt, T1 a1)

return format(Tuple.Create(fmt, a1));


static public string format(string fmt)

return format(Tuple.Create(fmt));

;


public class Program

unsafe public static void Main()

// System.Threading.Thread.Sleep(100000);
int z = 123;
int* y = &z;
IntPtr v = (IntPtr)y;
string s = FK0.Format.format("%p %d %d", v, *y, z);
Console.WriteLine(s);











share|improve this question

















  • 4




    Is there a particular reason you want to use this rather than the existing C# capabilities like string.Format?
    – mjwills
    Nov 9 at 11:49










  • Similar question: stackoverflow.com/questions/28155317/…
    – fk0
    Nov 9 at 11:58










  • Besides this huge amount of work that could be easily done with a string.Format call as @mjwills said, you can always create a public class Tuple<T1, T2, ....., T25> if you want. It won't solve the problem, though
    – Camilo Terevinto
    Nov 9 at 11:58






  • 1




    There's also a (undocumented?) keyword called __arglist which can be used to call varargs functions like printf. But I would recommend sticking to String.Format for code that's in a production environment.
    – Dirk
    Nov 9 at 12:04







  • 1




    There are like half a dozen questions in here, and the entire enterprise seems like a bad idea in the first place; if you want to call printf, just call the C++/CLI version of printf! Can you break this out into one simple question per question where each question has a clear, specific answer?
    – Eric Lippert
    Nov 9 at 21:49












up vote
-2
down vote

favorite
2









up vote
-2
down vote

favorite
2






2





I want to write function for C#, which will be similar to sprintf(3) and vsnprintf(3) functions in C (or similar to "format" command in Tcl). Currently I have some success with it, but still have a few questions.



Below is the source code. Class "Format" contains few public functions, which accepts variable arguments list (but no more than 7 arguments, see below) and produce formatted string. This is analog of sprintf(3) function. Also function "format" may accept C# tuple, which should hold format string in first element, and arguments for format string in other elements -- this is analog of vsprintf(3) function.



Unfortunately, here I have two major limitations:



1) I can't pass more than seven arguments to format() function, because arguments passed in C# tuple, and tuple can't have more than eight elements (first element is format string itself, this is needed because empty tuples unavailable in C#). I hope to obtain some suggestions, how can I improve my format() function to avoid this limitation.



2) Another major limitation, that not all types can be passed to C# template. Especially, I can't pass pointers to format function (in this case I will got following error: "error CS0306: The type 'int*' may not be used as a type argument"). This is C# limitation? It's possible to rewrite format() function(s) to avoid this limitation?



Another major inconvenience, is that I should open some particular library name, and use C function name, which is different for different operation systems:



  • for windows I should use "msvcrt.dll" and "_snprintf";


  • for linux I should use "libc.so.6" and "snprintf";


  • for linux on embedded platform C-library name might have different name...


It's possible to define which library should be opened at runtime, or extern functions marshalling might be determined only at compile time? So, I see two variants possible here:



1) I need produce different DLL's for different target platforms;



2) Or I can decide at runtime, which function name and libc-library should be used?



I not understood, how can I rewrite the code for first or second variant. Also it looks very inconvenient to have knowledge about libc name. Why just not call dlsym("snprintf") ?



Another question, I trying limit using of dynamic memory, but looks It's impossible to avoid allocating few String and StringBuilder classes on heap. This is looking scary, because when programming on C/C++ it's possible to do almost all work without dynamic memory allocation. May be somebody suggests me, how to improve format() functions to avoid dynamic memory allocation.



using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace FK0

using Args = ITuple;

public class Format

// const string LIBC = "msvcrt.dll"; //"libc.so";
// const string FUNC = "_snprintf";
const string LIBC = "libc.so.6";
const string FUNC = "snprintf";
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.I4)] int a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.I8)] long a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, double a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.LPStr)]string a1);

// function returns length of next format segment (string, copied as is, or single format spec.)
static private int parse_format(string fmt, int pos)

int p = fmt.IndexOf('%', pos);
if (p == -1) return fmt.Length - pos; // copy to end of string
else if (p != pos) return p - pos; // copy till %

char fmt_term = 'd','i','o','u','x','X','e','E','f','F','g','G','a','A','c','s','p','n','%' ;
int e = fmt.IndexOfAny(fmt_term, p + 1);
if (e == -1) throw new System.ArgumentException("invalid format string");
return e - p + 1; // format specifier length


// call real `snprintf(3)' from C-library, marshal arguments appropriately
static private int call_snprintf(ref StringBuilder res, int len, StringBuilder fmt, Object arg)
arg is IntPtr) // XXX can't pass pointer to template!!!
return snprintf(res, (IntPtr)len, fmt, ((IntPtr)arg).ToInt64());
//else if (arg.GetType()
else
return snprintf(res, (IntPtr)len, fmt, Convert.ToInt32(arg));


// vsnprintf-like function (accepts all arguments in tuple)
static public string format(Args args)

if (! (args[0] is string)) // check, that first argument is string
throw new System.ArgumentException("wrong string format type");

// first pass
// compute number of arguments, size of output string and max size of formatted output
string fmt = args[0].ToString();
StringBuilder ns = null, fs = new StringBuilder();
int total_len = 0, maxlen = 0, narg = 1;
int pos = 0;
while (pos < fmt.Length)
int len = parse_format(fmt, pos);
if (fmt[pos] == '%') // pass format specifier to snprintf(3)
fs.Clear(); fs.Append(fmt, pos, len);
int flen = call_snprintf(ref ns, 0, fs, args[narg]);
if (flen == -1) throw new System.ArgumentException("wrong format string");
total_len += flen;
if (flen > maxlen) maxlen = flen;
narg++;

else // compute size of literal part
total_len += len;

pos += len;


if (narg != args.Length)
throw new System.ArgumentException("incorrect # of arguments for format string");

// second pass
// print each argument separately
var result = new StringBuilder(total_len);
var part = new StringBuilder(maxlen + 1); // include terminating zero
pos = 0; narg = 1;
while (pos < fmt.Length)
int len = parse_format(fmt, pos);
if (fmt[pos] == '%') // pass format specifier to snprintf(3)
fs.Clear(); fs.Append(fmt, pos, len);
call_snprintf(ref part, part.Capacity, fs, args[narg++]);
result.Append(part);
Console.WriteLine(part);

else // copy literal part as is
result.Append(fmt, pos, len);

pos += len;


return result.ToString();


// C# have no vararg templates, as C++03, also max size of tuple limited to 8 elements,
// also impossible to create empty tuple, so maximum number arguments limited to 7 (plus format string as 0-th element).

static public string format<T1, T2, T3, T4, T5, T6, T7>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5, a6, a7));


static public string format<T1, T2, T3, T4, T5, T6>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5, a6));


static public string format<T1, T2, T3, T4, T5>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5));


static public string format<T1, T2, T3, T4>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4)

return format(Tuple.Create(fmt, a1, a2, a3, a4));


static public string format<T1, T2, T3>(string fmt, T1 a1, T2 a2, T3 a3)

return format(Tuple.Create(fmt, a1, a2, a3));


static public string format<T1, T2>(string fmt, T1 a1, T2 a2)

return format(Tuple.Create(fmt, a1, a2));


static public string format<T1>(string fmt, T1 a1)

return format(Tuple.Create(fmt, a1));


static public string format(string fmt)

return format(Tuple.Create(fmt));

;


public class Program

unsafe public static void Main()

// System.Threading.Thread.Sleep(100000);
int z = 123;
int* y = &z;
IntPtr v = (IntPtr)y;
string s = FK0.Format.format("%p %d %d", v, *y, z);
Console.WriteLine(s);











share|improve this question













I want to write function for C#, which will be similar to sprintf(3) and vsnprintf(3) functions in C (or similar to "format" command in Tcl). Currently I have some success with it, but still have a few questions.



Below is the source code. Class "Format" contains few public functions, which accepts variable arguments list (but no more than 7 arguments, see below) and produce formatted string. This is analog of sprintf(3) function. Also function "format" may accept C# tuple, which should hold format string in first element, and arguments for format string in other elements -- this is analog of vsprintf(3) function.



Unfortunately, here I have two major limitations:



1) I can't pass more than seven arguments to format() function, because arguments passed in C# tuple, and tuple can't have more than eight elements (first element is format string itself, this is needed because empty tuples unavailable in C#). I hope to obtain some suggestions, how can I improve my format() function to avoid this limitation.



2) Another major limitation, that not all types can be passed to C# template. Especially, I can't pass pointers to format function (in this case I will got following error: "error CS0306: The type 'int*' may not be used as a type argument"). This is C# limitation? It's possible to rewrite format() function(s) to avoid this limitation?



Another major inconvenience, is that I should open some particular library name, and use C function name, which is different for different operation systems:



  • for windows I should use "msvcrt.dll" and "_snprintf";


  • for linux I should use "libc.so.6" and "snprintf";


  • for linux on embedded platform C-library name might have different name...


It's possible to define which library should be opened at runtime, or extern functions marshalling might be determined only at compile time? So, I see two variants possible here:



1) I need produce different DLL's for different target platforms;



2) Or I can decide at runtime, which function name and libc-library should be used?



I not understood, how can I rewrite the code for first or second variant. Also it looks very inconvenient to have knowledge about libc name. Why just not call dlsym("snprintf") ?



Another question, I trying limit using of dynamic memory, but looks It's impossible to avoid allocating few String and StringBuilder classes on heap. This is looking scary, because when programming on C/C++ it's possible to do almost all work without dynamic memory allocation. May be somebody suggests me, how to improve format() functions to avoid dynamic memory allocation.



using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace FK0

using Args = ITuple;

public class Format

// const string LIBC = "msvcrt.dll"; //"libc.so";
// const string FUNC = "_snprintf";
const string LIBC = "libc.so.6";
const string FUNC = "snprintf";
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.I4)] int a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.I8)] long a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, double a1);
[DllImport(LIBC, EntryPoint=FUNC)] static private extern int snprintf(StringBuilder result, [MarshalAs(UnmanagedType.SysInt)] IntPtr size, StringBuilder format, [MarshalAs(UnmanagedType.LPStr)]string a1);

// function returns length of next format segment (string, copied as is, or single format spec.)
static private int parse_format(string fmt, int pos)

int p = fmt.IndexOf('%', pos);
if (p == -1) return fmt.Length - pos; // copy to end of string
else if (p != pos) return p - pos; // copy till %

char fmt_term = 'd','i','o','u','x','X','e','E','f','F','g','G','a','A','c','s','p','n','%' ;
int e = fmt.IndexOfAny(fmt_term, p + 1);
if (e == -1) throw new System.ArgumentException("invalid format string");
return e - p + 1; // format specifier length


// call real `snprintf(3)' from C-library, marshal arguments appropriately
static private int call_snprintf(ref StringBuilder res, int len, StringBuilder fmt, Object arg)
arg is IntPtr) // XXX can't pass pointer to template!!!
return snprintf(res, (IntPtr)len, fmt, ((IntPtr)arg).ToInt64());
//else if (arg.GetType()
else
return snprintf(res, (IntPtr)len, fmt, Convert.ToInt32(arg));


// vsnprintf-like function (accepts all arguments in tuple)
static public string format(Args args)

if (! (args[0] is string)) // check, that first argument is string
throw new System.ArgumentException("wrong string format type");

// first pass
// compute number of arguments, size of output string and max size of formatted output
string fmt = args[0].ToString();
StringBuilder ns = null, fs = new StringBuilder();
int total_len = 0, maxlen = 0, narg = 1;
int pos = 0;
while (pos < fmt.Length)
int len = parse_format(fmt, pos);
if (fmt[pos] == '%') // pass format specifier to snprintf(3)
fs.Clear(); fs.Append(fmt, pos, len);
int flen = call_snprintf(ref ns, 0, fs, args[narg]);
if (flen == -1) throw new System.ArgumentException("wrong format string");
total_len += flen;
if (flen > maxlen) maxlen = flen;
narg++;

else // compute size of literal part
total_len += len;

pos += len;


if (narg != args.Length)
throw new System.ArgumentException("incorrect # of arguments for format string");

// second pass
// print each argument separately
var result = new StringBuilder(total_len);
var part = new StringBuilder(maxlen + 1); // include terminating zero
pos = 0; narg = 1;
while (pos < fmt.Length)
int len = parse_format(fmt, pos);
if (fmt[pos] == '%') // pass format specifier to snprintf(3)
fs.Clear(); fs.Append(fmt, pos, len);
call_snprintf(ref part, part.Capacity, fs, args[narg++]);
result.Append(part);
Console.WriteLine(part);

else // copy literal part as is
result.Append(fmt, pos, len);

pos += len;


return result.ToString();


// C# have no vararg templates, as C++03, also max size of tuple limited to 8 elements,
// also impossible to create empty tuple, so maximum number arguments limited to 7 (plus format string as 0-th element).

static public string format<T1, T2, T3, T4, T5, T6, T7>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5, a6, a7));


static public string format<T1, T2, T3, T4, T5, T6>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5, a6));


static public string format<T1, T2, T3, T4, T5>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5)

return format(Tuple.Create(fmt, a1, a2, a3, a4, a5));


static public string format<T1, T2, T3, T4>(string fmt, T1 a1, T2 a2, T3 a3, T4 a4)

return format(Tuple.Create(fmt, a1, a2, a3, a4));


static public string format<T1, T2, T3>(string fmt, T1 a1, T2 a2, T3 a3)

return format(Tuple.Create(fmt, a1, a2, a3));


static public string format<T1, T2>(string fmt, T1 a1, T2 a2)

return format(Tuple.Create(fmt, a1, a2));


static public string format<T1>(string fmt, T1 a1)

return format(Tuple.Create(fmt, a1));


static public string format(string fmt)

return format(Tuple.Create(fmt));

;


public class Program

unsafe public static void Main()

// System.Threading.Thread.Sleep(100000);
int z = 123;
int* y = &z;
IntPtr v = (IntPtr)y;
string s = FK0.Format.format("%p %d %d", v, *y, z);
Console.WriteLine(s);








c# .net printf clr coreclr






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 9 at 11:48









fk0

566




566







  • 4




    Is there a particular reason you want to use this rather than the existing C# capabilities like string.Format?
    – mjwills
    Nov 9 at 11:49










  • Similar question: stackoverflow.com/questions/28155317/…
    – fk0
    Nov 9 at 11:58










  • Besides this huge amount of work that could be easily done with a string.Format call as @mjwills said, you can always create a public class Tuple<T1, T2, ....., T25> if you want. It won't solve the problem, though
    – Camilo Terevinto
    Nov 9 at 11:58






  • 1




    There's also a (undocumented?) keyword called __arglist which can be used to call varargs functions like printf. But I would recommend sticking to String.Format for code that's in a production environment.
    – Dirk
    Nov 9 at 12:04







  • 1




    There are like half a dozen questions in here, and the entire enterprise seems like a bad idea in the first place; if you want to call printf, just call the C++/CLI version of printf! Can you break this out into one simple question per question where each question has a clear, specific answer?
    – Eric Lippert
    Nov 9 at 21:49












  • 4




    Is there a particular reason you want to use this rather than the existing C# capabilities like string.Format?
    – mjwills
    Nov 9 at 11:49










  • Similar question: stackoverflow.com/questions/28155317/…
    – fk0
    Nov 9 at 11:58










  • Besides this huge amount of work that could be easily done with a string.Format call as @mjwills said, you can always create a public class Tuple<T1, T2, ....., T25> if you want. It won't solve the problem, though
    – Camilo Terevinto
    Nov 9 at 11:58






  • 1




    There's also a (undocumented?) keyword called __arglist which can be used to call varargs functions like printf. But I would recommend sticking to String.Format for code that's in a production environment.
    – Dirk
    Nov 9 at 12:04







  • 1




    There are like half a dozen questions in here, and the entire enterprise seems like a bad idea in the first place; if you want to call printf, just call the C++/CLI version of printf! Can you break this out into one simple question per question where each question has a clear, specific answer?
    – Eric Lippert
    Nov 9 at 21:49







4




4




Is there a particular reason you want to use this rather than the existing C# capabilities like string.Format?
– mjwills
Nov 9 at 11:49




Is there a particular reason you want to use this rather than the existing C# capabilities like string.Format?
– mjwills
Nov 9 at 11:49












Similar question: stackoverflow.com/questions/28155317/…
– fk0
Nov 9 at 11:58




Similar question: stackoverflow.com/questions/28155317/…
– fk0
Nov 9 at 11:58












Besides this huge amount of work that could be easily done with a string.Format call as @mjwills said, you can always create a public class Tuple<T1, T2, ....., T25> if you want. It won't solve the problem, though
– Camilo Terevinto
Nov 9 at 11:58




Besides this huge amount of work that could be easily done with a string.Format call as @mjwills said, you can always create a public class Tuple<T1, T2, ....., T25> if you want. It won't solve the problem, though
– Camilo Terevinto
Nov 9 at 11:58




1




1




There's also a (undocumented?) keyword called __arglist which can be used to call varargs functions like printf. But I would recommend sticking to String.Format for code that's in a production environment.
– Dirk
Nov 9 at 12:04





There's also a (undocumented?) keyword called __arglist which can be used to call varargs functions like printf. But I would recommend sticking to String.Format for code that's in a production environment.
– Dirk
Nov 9 at 12:04





1




1




There are like half a dozen questions in here, and the entire enterprise seems like a bad idea in the first place; if you want to call printf, just call the C++/CLI version of printf! Can you break this out into one simple question per question where each question has a clear, specific answer?
– Eric Lippert
Nov 9 at 21:49




There are like half a dozen questions in here, and the entire enterprise seems like a bad idea in the first place; if you want to call printf, just call the C++/CLI version of printf! Can you break this out into one simple question per question where each question has a clear, specific answer?
– Eric Lippert
Nov 9 at 21:49












1 Answer
1






active

oldest

votes

















up vote
1
down vote













Using the C# params parameter modifier, you can have the arguments to a method passed as an array:



public static string format(string format, params object args) 
var n = 0;
return String.Format(format.Replace(new Regex(@"%[+-0-9.]*[a-z]"), m => $"n++"), args);



Now you can call it like:



Console.WriteLine(Format.format("test: %s %x", "this", 23));


Of course, this version of format just dumps all arguments in their default format, you would need to process each format specifier in a real implementation.






share|improve this answer




















    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53225180%2fprintf-like-function-for-c-sharp%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote













    Using the C# params parameter modifier, you can have the arguments to a method passed as an array:



    public static string format(string format, params object args) 
    var n = 0;
    return String.Format(format.Replace(new Regex(@"%[+-0-9.]*[a-z]"), m => $"n++"), args);



    Now you can call it like:



    Console.WriteLine(Format.format("test: %s %x", "this", 23));


    Of course, this version of format just dumps all arguments in their default format, you would need to process each format specifier in a real implementation.






    share|improve this answer
























      up vote
      1
      down vote













      Using the C# params parameter modifier, you can have the arguments to a method passed as an array:



      public static string format(string format, params object args) 
      var n = 0;
      return String.Format(format.Replace(new Regex(@"%[+-0-9.]*[a-z]"), m => $"n++"), args);



      Now you can call it like:



      Console.WriteLine(Format.format("test: %s %x", "this", 23));


      Of course, this version of format just dumps all arguments in their default format, you would need to process each format specifier in a real implementation.






      share|improve this answer






















        up vote
        1
        down vote










        up vote
        1
        down vote









        Using the C# params parameter modifier, you can have the arguments to a method passed as an array:



        public static string format(string format, params object args) 
        var n = 0;
        return String.Format(format.Replace(new Regex(@"%[+-0-9.]*[a-z]"), m => $"n++"), args);



        Now you can call it like:



        Console.WriteLine(Format.format("test: %s %x", "this", 23));


        Of course, this version of format just dumps all arguments in their default format, you would need to process each format specifier in a real implementation.






        share|improve this answer












        Using the C# params parameter modifier, you can have the arguments to a method passed as an array:



        public static string format(string format, params object args) 
        var n = 0;
        return String.Format(format.Replace(new Regex(@"%[+-0-9.]*[a-z]"), m => $"n++"), args);



        Now you can call it like:



        Console.WriteLine(Format.format("test: %s %x", "this", 23));


        Of course, this version of format just dumps all arguments in their default format, you would need to process each format specifier in a real implementation.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 9 at 21:15









        NetMage

        12.7k11734




        12.7k11734



























             

            draft saved


            draft discarded















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53225180%2fprintf-like-function-for-c-sharp%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Kleinkühnau

            Makov (Slowakei)

            Deutsches Schauspielhaus