Commit 309963f8 authored by Kirill Smelkov's avatar Kirill Smelkov

fmt: New package

With fmt.sprintf() and fmt.errorf() to format strings and errors. The
code was extracted from wcfs in wendelin.core .

Pyx/nogil only.
parent 9ea53ec6
...@@ -9,6 +9,8 @@ include golang/context.cpp ...@@ -9,6 +9,8 @@ include golang/context.cpp
include golang/cxx.h include golang/cxx.h
include golang/errors.h include golang/errors.h
include golang/errors.cpp include golang/errors.cpp
include golang/fmt.h
include golang/fmt.cpp
include golang/sync.h include golang/sync.h
include golang/sync.cpp include golang/sync.cpp
include golang/time.h include golang/time.h
......
...@@ -227,6 +227,8 @@ cdef extern from * nogil: ...@@ -227,6 +227,8 @@ cdef extern from * nogil:
extern void _test_defer(); extern void _test_defer();
extern void _test_refptr(); extern void _test_refptr();
extern void _test_global(); extern void _test_global();
extern void _test_fmt_sprintf_cpp();
extern void _test_fmt_errorf_cpp();
extern void _test_sync_once_cpp(); extern void _test_sync_once_cpp();
""" """
void _test_chan_cpp_refcount() except +topyexc void _test_chan_cpp_refcount() except +topyexc
...@@ -240,6 +242,8 @@ cdef extern from * nogil: ...@@ -240,6 +242,8 @@ cdef extern from * nogil:
void _test_defer() except +topyexc void _test_defer() except +topyexc
void _test_refptr() except +topyexc void _test_refptr() except +topyexc
void _test_global() except +topyexc void _test_global() except +topyexc
void _test_fmt_sprintf_cpp() except +topyexc
void _test_fmt_errorf_cpp() except +topyexc
void _test_sync_once_cpp() except +topyexc void _test_sync_once_cpp() except +topyexc
def test_chan_cpp_refcount(): def test_chan_cpp_refcount():
with nogil: with nogil:
...@@ -274,6 +278,12 @@ def test_refptr(): ...@@ -274,6 +278,12 @@ def test_refptr():
def test_global(): def test_global():
with nogil: with nogil:
_test_global() _test_global()
def test_fmt_sprintf_cpp(): # TODO move -> _fmt_test.pyx
with nogil:
_test_fmt_sprintf_cpp()
def test_fmt_errorf_cpp():
with nogil:
_test_fmt_errorf_cpp()
def test_sync_once_cpp(): # TODO move -> _sync_test.pyx def test_sync_once_cpp(): # TODO move -> _sync_test.pyx
with nogil: with nogil:
_test_sync_once_cpp() _test_sync_once_cpp()
......
// Copyright (C) 2019 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// Package fmt mirrors Go package fmt.
// See fmt.h for package overview.
// TODO consider reusing https://github.com/fmtlib/fmt
#include "golang/fmt.h"
#include "golang/errors.h"
#include <stdarg.h>
#include <stdio.h>
// golang::fmt::
namespace golang {
namespace fmt {
static
string _vsprintf(const char *format, va_list argp) {
// based on https://stackoverflow.com/a/26221725/9456786
va_list argp2;
va_copy(argp2, argp);
size_t nchar = ::vsnprintf(NULL, 0, format, argp2);
va_end(argp2);
std::unique_ptr<char[]> buf( new char[nchar /*for \0*/+1] );
vsnprintf(buf.get(), /*size limit in bytes including \0*/nchar+1, format, argp);
return string(buf.get(), buf.get() + nchar); // without trailing '\0'
}
string sprintf(const string &format, ...) {
va_list argp;
va_start(argp, format);
string str = fmt::_vsprintf(format.c_str(), argp);
va_end(argp);
return str;
}
string sprintf(const char *format, ...) {
va_list argp;
va_start(argp, format);
string str = fmt::_vsprintf(format, argp);
va_end(argp);
return str;
}
error errorf(const string &format, ...) {
va_list argp;
va_start(argp, format);
error err = errors::New(fmt::_vsprintf(format.c_str(), argp));
va_end(argp);
return err;
}
error errorf(const char *format, ...) {
va_list argp;
va_start(argp, format);
error err = errors::New(fmt::_vsprintf(format, argp));
va_end(argp);
return err;
}
}} // golang::fmt::
#ifndef _NXD_LIBGOLANG_FMT_H
#define _NXD_LIBGOLANG_FMT_H
// Copyright (C) 2019 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// Package fmt mirrors Go package fmt.
//
// - `sprintf` formats text into string.
// - `errorf` formats text into error.
//
// NOTE: formatting rules are those of libc, not Go.
//
// See also https://golang.org/pkg/fmt for Go fmt package documentation.
#include <golang/libgolang.h>
// golang::fmt::
namespace golang {
namespace fmt {
// sprintf formats text into string.
LIBGOLANG_API string sprintf(const string &format, ...);
// `errorf` formats text into error.
LIBGOLANG_API error errorf (const string &format, ...);
// `const char *` overloads just to catch format mistakes as
// __attribute__(format) does not work with std::string.
LIBGOLANG_API string sprintf(const char *format, ...)
__attribute__ ((format (printf, 1, 2)));
LIBGOLANG_API error errorf (const char *format, ...)
__attribute__ ((format (printf, 1, 2)));
}} // golang::fmt::
#endif // _NXD_LIBGOLANG_FMT_H
# cython: language_level=2
# Copyright (C) 2019 Nexedi SA and Contributors.
# Kirill Smelkov <kirr@nexedi.com>
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
"""Package fmt mirrors Go package fmt.
- `sprintf` formats text into string.
- `errorf` formats text into error.
NOTE: formatting rules are those of libc, not Go.
See also https://golang.org/pkg/fmt for Go fmt package documentation.
"""
from golang cimport string, error
cdef extern from "golang/fmt.h" namespace "golang::fmt" nogil:
string sprintf(const string &format, ...)
error errorf (const string &format, ...)
string sprintf(const char *format, ...)
error errorf (const char *format, ...)
...@@ -174,6 +174,8 @@ def Extension(name, sources, **kw): ...@@ -174,6 +174,8 @@ def Extension(name, sources, **kw):
dependv.append('%s/golang/errors.h' % pygo) dependv.append('%s/golang/errors.h' % pygo)
dependv.append('%s/golang/errors.pxd' % pygo) dependv.append('%s/golang/errors.pxd' % pygo)
dependv.append('%s/golang/_errors.pxd' % pygo) dependv.append('%s/golang/_errors.pxd' % pygo)
dependv.append('%s/golang/fmt.h' % pygo)
dependv.append('%s/golang/fmt.pxd' % pygo)
dependv.append('%s/golang/sync.h' % pygo) dependv.append('%s/golang/sync.h' % pygo)
dependv.append('%s/golang/sync.pxd' % pygo) dependv.append('%s/golang/sync.pxd' % pygo)
dependv.append('%s/golang/_sync.pxd' % pygo) dependv.append('%s/golang/_sync.pxd' % pygo)
......
...@@ -20,12 +20,14 @@ ...@@ -20,12 +20,14 @@
// Test that exercises C++-level libgolang.h API and functionality. // Test that exercises C++-level libgolang.h API and functionality.
#include "golang/libgolang.h" #include "golang/libgolang.h"
#include "golang/fmt.h"
#include "golang/sync.h" #include "golang/sync.h"
#include "golang/time.h" #include "golang/time.h"
#include <stdio.h> #include <stdio.h>
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <sstream>
#include <string.h> #include <string.h>
#include <vector> #include <vector>
using namespace golang; using namespace golang;
...@@ -682,6 +684,71 @@ void _test_global() { ...@@ -682,6 +684,71 @@ void _test_global() {
ASSERT(obj->refcnt() == 3); ASSERT(obj->refcnt() == 3);
} }
// ---- fmt:: ----
// std::
namespace std {
// string -> string (not in STL, huh ?!)
string to_string(const string& s) { return s; }
// vector<T> -> string
template<typename T>
string to_string(const vector<T>& v) {
std::ostringstream ss;
ss << "[";
int i = 0;
for (auto x : v) {
if (i++ != 0)
ss << " ";
ss << x << ",";
}
ss << "]";
return ss.str();
}
} // std::
template<typename T, typename U>
void __assert_eq(const string &expr, const T &have, const U &want) {
if (have != want) {
string emsg = expr + "\n";
emsg += "have: '" + std::to_string(have) + "'\n";
emsg += "want: '" + std::to_string(want) + "'";
panic(strdup(emsg.c_str())); // XXX strdup because panic just saves char* pointer
}
};
#define ASSERT_EQ(A, B) __assert_eq(#A, A, B)
void _test_fmt_sprintf_cpp() {
// NOTE not using vargs helper, since sprintf itself uses vargs and we want
// to test varg logic there for correctness too.
ASSERT_EQ(fmt::sprintf("hello world") , "hello world");
ASSERT_EQ(fmt::sprintf("hello %d zzz", 123) , "hello 123 zzz");
ASSERT_EQ(fmt::sprintf("%s %s: %s", "read", "myfile", "myerror") , "read myfile: myerror");
// with string format (not `const char *`)
const char *myerror = "myerror";
string f = "%s %s: %s";
const char *myfile = "myfile";
ASSERT_EQ(fmt::sprintf(f, "read", myfile, myerror) , "read myfile: myerror");
}
void _test_fmt_errorf_cpp() {
ASSERT_EQ(fmt::errorf("hello world")->Error() , "hello world");
ASSERT_EQ(fmt::errorf("hello %d zzz", 123)->Error() , "hello 123 zzz");
ASSERT_EQ(fmt::errorf("%s %s: %s", "read", "myfile", "myerror")->Error() , "read myfile: myerror");
// with string format (not `const char *`)
const char *myerror = "myerror";
string f = "%s %s: %s";
const char *myfile = "myfile";
ASSERT_EQ(fmt::errorf(f, "read", myfile, myerror)->Error() , "read myfile: myerror");
}
// ---- sync:: ---- // ---- sync:: ----
// verify that sync::Once works. // verify that sync::Once works.
......
...@@ -196,6 +196,7 @@ setup( ...@@ -196,6 +196,7 @@ setup(
['golang/runtime/libgolang.cpp', ['golang/runtime/libgolang.cpp',
'golang/context.cpp', 'golang/context.cpp',
'golang/errors.cpp', 'golang/errors.cpp',
'golang/fmt.cpp',
'golang/sync.cpp', 'golang/sync.cpp',
'golang/time.cpp'], 'golang/time.cpp'],
depends = [ depends = [
...@@ -203,6 +204,7 @@ setup( ...@@ -203,6 +204,7 @@ setup(
'golang/context.h', 'golang/context.h',
'golang/cxx.h', 'golang/cxx.h',
'golang/errors.h', 'golang/errors.h',
'golang/fmt.h',
'golang/sync.h', 'golang/sync.h',
'golang/time.h'], 'golang/time.h'],
include_dirs = ['.', '3rdparty/include'], include_dirs = ['.', '3rdparty/include'],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment