Add liblutok a lightweight C++ API for lua.

It is added an INTERNALLIB and not installed.  It will be used by kyua.

This is a preparatory commit for D24103.

Reviewed by:	emaste
Obtained from:	CheriBSD
Sponsored by:	DARPA
This commit is contained in:
Brooks Davis 2020-03-23 18:26:23 +00:00
commit c697fb7f7c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=359255
59 changed files with 6264 additions and 0 deletions

21
contrib/lutok/.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
*.la
*.lo
*.o
*_test
.deps
.libs
Doxyfile
Makefile
Makefile.in
aclocal.m4
api-docs
autom4te.cache
config.h
config.h.in
config.log
config.status
configure
libtool
lutok.pc
stamp-h1

25
contrib/lutok/.travis.yml Normal file
View file

@ -0,0 +1,25 @@
language: cpp
compiler:
- gcc
- clang
before_install:
- ./admin/travis-install-deps.sh
env:
- ARCH=amd64 AS_ROOT=no
- ARCH=amd64 AS_ROOT=yes
- ARCH=i386 AS_ROOT=no
matrix:
exclude:
- compiler: clang
env: ARCH=i386 AS_ROOT=no
script:
- ./admin/travis-build.sh
notifications:
email:
- lutok-log@googlegroups.com

1
contrib/lutok/AUTHORS Normal file
View file

@ -0,0 +1 @@
* Julio Merino <jmmv@google.com>

27
contrib/lutok/COPYING Normal file
View file

@ -0,0 +1,27 @@
Copyright 2011, 2012 Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Google Inc. nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

53
contrib/lutok/Doxyfile.in Normal file
View file

@ -0,0 +1,53 @@
# Copyright 2010 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
BUILTIN_STL_SUPPORT = YES
ENABLE_PREPROCESSING = YES
EXTRACT_ANON_NSPACES = YES
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
EXPAND_ONLY_PREDEF = YES
FILE_PATTERNS = *.cpp *.hpp *.ipp
GENERATE_LATEX = NO
GENERATE_TAGFILE = @top_builddir@/api-docs/api-docs.tag
INPUT = @top_srcdir@
INPUT_ENCODING = ISO-8859-1
JAVADOC_AUTOBRIEF = YES
MACRO_EXPANSION = YES
OUTPUT_DIRECTORY = @top_builddir@/api-docs
OUTPUT_LANGUAGE = English
PREDEFINED = "UTILS_UNUSED_PARAM(name)=unused_ ## name"
PROJECT_NAME = "@PACKAGE_NAME@"
PROJECT_NUMBER = @VERSION@
QUIET = YES
RECURSIVE = NO
SHORT_NAMES = YES # Cope with gnutar limitations during 'make dist'.
SORT_BY_SCOPE_NAME = YES
SORT_MEMBERS_CTORS_1ST = YES
WARN_NO_PARAMDOC = YES

181
contrib/lutok/INSTALL Normal file
View file

@ -0,0 +1,181 @@
Introduction
============
Lutok uses the GNU Automake, GNU Autoconf and GNU Libtool utilities as
its build system. These are used only when compiling the library from
the source code package. If you want to install Lutok from a binary
package, you do not need to read this document.
For the impatient:
$ ./configure
$ make
$ make check
Gain root privileges
# make install
Drop root privileges
$ make installcheck
Or alternatively, install as a regular user into your home directory:
$ ./configure --prefix ~/local
$ make
$ make check
$ make install
$ make installcheck
Dependencies
============
To build and use Lutok successfully you need:
* A standards-compliant C++ complier.
* Lua 5.1 or greater.
* pkg-config.
Optionally, if you want to build and run the tests (recommended), you
need:
* Kyua 0.5 or greater.
* ATF 0.15 or greater.
If you are building Lutok from the code on the repository, you will also
need the following tools:
* GNU Autoconf.
* GNU Automake.
* GNU Libtool.
Regenerating the build system
=============================
This is not necessary if you are building from a formal release
distribution file.
On the other hand, if you are building Lutok from code extracted from
the repository, you must first regenerate the files used by the build
system. You will also need to do this if you modify configure.ac,
Makefile.am or any of the other build system files. To do this, simply
run:
$ autoreconf -i -s
If ATF is installed in a different prefix than Autoconf, you will also
need to tell autoreconf where the ATF M4 macros are located. Otherwise,
the configure script will be incomplete and will show confusing syntax
errors mentioning, for example, ATF_CHECK_SH. To fix this, you have
to run autoreconf in the following manner, replacing '<atf-prefix>' with
the appropriate path:
$ autoreconf -i -s -I <atf-prefix>/share/aclocal
General build procedure
=======================
To build and install the source package, you must follow these steps:
1. Configure the sources to adapt to your operating system. This is
done using the 'configure' script located on the sources' top
directory, and it is usually invoked without arguments unless you
want to change the installation prefix. More details on this
procedure are given on a later section.
2. Build the sources to generate the binaries and scripts. Simply run
'make' on the sources' top directory after configuring them. No
problems should arise.
3. Install the library by running 'make install'. You may need to
become root to issue this step.
4. Issue any manual installation steps that may be required. These are
described later in their own section.
5. Check that the installed library works by running 'make
installcheck'. You do not need to be root to do this.
Configuration flags
===================
The most common, standard flags given to 'configure' are:
* --prefix=directory
Possible values: Any path
Default: /usr/local
Specifies where the library (binaries and all associated files) will
be installed.
* --help
Shows information about all available flags and exits immediately,
without running any configuration tasks.
The following flags are specific to Lutok's 'configure' script:
* --enable-developer
Possible values: yes, no
Default: 'yes' in Git HEAD builds; 'no' in formal releases.
Enables several features useful for development, such as the inclusion
of debugging symbols in all objects or the enforcement of compilation
warnings.
The compiler will be executed with an exhaustive collection of warning
detection features regardless of the value of this flag. However, such
warnings are only fatal when --enable-developer is 'yes'.
* --with-atf
Possible values: yes, no, auto.
Default: auto.
Enables usage of ATF to build (and later install) the tests.
Setting this to 'yes' causes the configure script to look for ATF
unconditionally and abort if not found. Setting this to 'auto' lets
configure perform the best decision based on availability of ATF.
Setting this to 'no' explicitly disables ATF usage.
When support for tests is enabled, the build process will generate the
test programs and will later install them into the tests tree.
Running 'make check' or 'make installcheck' from within the source
directory will cause these tests to be run with Kyua (assuming it is
also installed).
* --with-doxygen
Possible values: yes, no, auto or a path.
Default: auto.
Enables usage of Doxygen to generate documentation for internal APIs.
Setting this to 'yes' causes the configure script to look for Doxygen
unconditionally and abort if not found. Setting this to 'auto' lets
configure perform the best decision based on availability of Doxygen.
Setting this to 'no' explicitly disables Doxygen usage. And, lastly,
setting this to a path forces configure to use a specific Doxygen
binary, which must exist.
When support for Doxygen is enabled, the build process will generate
HTML documentation for the Lutok API. This documentation will later
be installed in the HTML directory specified by the configure script.
You can change the location of the HTML documents by providing your
desired override with the '--htmldir' flag to the configure script.
Run the tests!
==============
Lastly, after a successful installation (and assuming you built the
sources with support for ATF), you should periodically run the tests
from the final location to ensure things remain stable. Do so as
follows:
$ kyua test -k /usr/local/tests/lutok/Kyuafile
And if you see any tests fail, do not hesitate to report them in:
https://github.com/jmmv/lutok/issues/
Thank you!

11
contrib/lutok/Kyuafile Normal file
View file

@ -0,0 +1,11 @@
syntax("kyuafile", 1)
test_suite("lutok")
atf_test_program{name="c_gate_test"}
atf_test_program{name="debug_test"}
atf_test_program{name="examples_test"}
atf_test_program{name="exceptions_test"}
atf_test_program{name="operations_test"}
atf_test_program{name="stack_cleaner_test"}
atf_test_program{name="state_test"}

221
contrib/lutok/Makefile.am Normal file
View file

@ -0,0 +1,221 @@
# Copyright 2010 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
ACLOCAL_AMFLAGS = -I m4
doc_DATA = AUTHORS COPYING NEWS README
noinst_DATA = INSTALL README
EXTRA_DIST = $(doc_DATA) INSTALL README
LUTOK_CFLAGS = -I$(srcdir)/include $(LUA_CFLAGS)
LUTOK_LIBS = liblutok.la $(LUA_LIBS)
pkginclude_HEADERS = c_gate.hpp
pkginclude_HEADERS += debug.hpp
pkginclude_HEADERS += exceptions.hpp
pkginclude_HEADERS += operations.hpp
pkginclude_HEADERS += stack_cleaner.hpp
pkginclude_HEADERS += state.hpp
pkginclude_HEADERS += state.ipp
pkginclude_HEADERS += test_utils.hpp
EXTRA_DIST += include/lutok/README
EXTRA_DIST += include/lutok/c_gate.hpp
EXTRA_DIST += include/lutok/debug.hpp
EXTRA_DIST += include/lutok/exceptions.hpp
EXTRA_DIST += include/lutok/operations.hpp
EXTRA_DIST += include/lutok/stack_cleaner.hpp
EXTRA_DIST += include/lutok/state.hpp
EXTRA_DIST += include/lutok/state.ipp
lib_LTLIBRARIES = liblutok.la
liblutok_la_SOURCES = c_gate.cpp
liblutok_la_SOURCES += c_gate.hpp
liblutok_la_SOURCES += debug.cpp
liblutok_la_SOURCES += debug.hpp
liblutok_la_SOURCES += exceptions.cpp
liblutok_la_SOURCES += exceptions.hpp
liblutok_la_SOURCES += operations.cpp
liblutok_la_SOURCES += operations.hpp
liblutok_la_SOURCES += stack_cleaner.cpp
liblutok_la_SOURCES += stack_cleaner.hpp
liblutok_la_SOURCES += state.cpp
liblutok_la_SOURCES += state.hpp
liblutok_la_SOURCES += state.ipp
liblutok_la_SOURCES += test_utils.hpp
liblutok_la_CPPFLAGS = $(LUTOK_CFLAGS)
liblutok_la_LDFLAGS = -version-info 3:0:0
liblutok_la_LIBADD = $(LUA_LIBS)
pkgconfig_DATA = lutok.pc
CLEANFILES = lutok.pc
EXTRA_DIST += lutok.pc.in
lutok.pc: $(srcdir)/lutok.pc.in Makefile
$(AM_V_GEN)sed -e 's#__INCLUDEDIR__#$(includedir)#g' \
-e 's#__LIBDIR__#$(libdir)#g' \
-e 's#__LUA_CFLAGS__#$(LUA_CFLAGS)#g' \
-e 's#__LUA_LIBS__#$(LUA_LIBS)#g' \
-e 's#__VERSION__#$(PACKAGE_VERSION)#g' \
<$(srcdir)/lutok.pc.in >lutok.pc.tmp; \
mv lutok.pc.tmp lutok.pc
CLEAN_TARGETS =
DIST_HOOKS =
PHONY_TARGETS =
examplesdir = $(docdir)/examples
examples_DATA = examples/Makefile
examples_DATA += examples/bindings.cpp
examples_DATA += examples/hello.cpp
examples_DATA += examples/interpreter.cpp
examples_DATA += examples/raii.cpp
EXTRA_DIST += $(examples_DATA)
if WITH_ATF
tests_DATA = Kyuafile
EXTRA_DIST += $(tests_DATA)
tests_PROGRAMS = c_gate_test
c_gate_test_SOURCES = c_gate_test.cpp test_utils.hpp
c_gate_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
c_gate_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
tests_PROGRAMS += debug_test
debug_test_SOURCES = debug_test.cpp test_utils.hpp
debug_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
debug_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
tests_SCRIPTS = examples_test
CLEANFILES += examples_test
EXTRA_DIST += examples_test.sh
examples_test: $(srcdir)/examples_test.sh
$(AM_V_GEN)sed -e 's,__ATF_SH__,$(ATF_SH),g' \
-e 's,__CXX__,$(CXX),g' \
-e 's,__EXAMPLESDIR__,$(examplesdir),g' \
-e 's,__LIBDIR__,$(libdir),g' \
<$(srcdir)/examples_test.sh >examples_test.tmp; \
chmod +x examples_test.tmp; \
rm -f examples_test; \
mv examples_test.tmp examples_test
tests_PROGRAMS += exceptions_test
exceptions_test_SOURCES = exceptions_test.cpp
exceptions_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
exceptions_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
tests_PROGRAMS += operations_test
operations_test_SOURCES = operations_test.cpp test_utils.hpp
operations_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
operations_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
tests_PROGRAMS += stack_cleaner_test
stack_cleaner_test_SOURCES = stack_cleaner_test.cpp test_utils.hpp
stack_cleaner_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
stack_cleaner_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
tests_PROGRAMS += state_test
state_test_SOURCES = state_test.cpp test_utils.hpp
state_test_CXXFLAGS = $(LUTOK_CFLAGS) $(ATF_CXX_CFLAGS)
state_test_LDADD = $(LUTOK_LIBS) $(ATF_CXX_LIBS)
if HAVE_KYUA
check-local: check-kyua
PHONY_TARGETS += check-kyua
check-kyua:
$(TESTS_ENVIRONMENT) kyua test \
--kyuafile='$(top_srcdir)/Kyuafile' --build-root='$(top_builddir)'
installcheck-local: installcheck-kyua
PHONY_TARGETS += installcheck-kyua
installcheck-kyua:
cd $(testsdir) && $(TESTS_ENVIRONMENT) kyua test
endif
else
DIST_HOOKS += dist-no-atf
PHONY_TARGETS += dist-no-atf
dist-no-atf:
@echo "Sorry; cannot build a distfile without atf"
@false
endif
if WITH_DOXYGEN
# This should probably be html-local, but it seems better to generate the
# documentation in all cases to get warnings about missing documentation every
# time the code is modified. (And, after all, the documentation is not
# installed so generating it unconditionally is not a big problem.)
all-local: api-docs/api-docs.tag
api-docs/api-docs.tag: $(builddir)/Doxyfile $(SOURCES)
$(AM_V_GEN)rm -rf api-docs; \
mkdir -p api-docs; \
${DOXYGEN} $(builddir)/Doxyfile 2>&1 | tee api-docs/warnings; \
rm -f api-docs/html/installdox
api-docs/html: api-docs/api-docs.tag
CLEAN_TARGETS += clean-api-docs
clean-api-docs:
rm -rf api-docs
EXTRA_DIST += api-docs/html
else
DIST_HOOKS += dist-no-doxygen
PHONY_TARGETS += dist-no-doxygen
dist-no-doxygen:
@echo "Sorry; cannot build a distfile without Doxygen"
@false
endif
install-data-local: install-api-docs
install-api-docs: install-docDATA
@echo "Installing HTML documentation into $(DESTDIR)$(htmldir)"
@if [ -d api-docs/html ]; then \
test -z "$(htmldir)" || $(MKDIR_P) "$(DESTDIR)$(htmldir)"; \
( cd api-docs/html && tar cf - . ) | \
( cd "$(DESTDIR)$(htmldir)" && tar xf - ); \
elif [ -d "$(srcdir)/api-docs/html" ]; then \
test -z "$(htmldir)" || $(MKDIR_P) "$(DESTDIR)$(htmldir)"; \
( cd "$(srcdir)/api-docs/html" && tar cf - . ) | \
( cd "$(DESTDIR)$(htmldir)" && tar xf - ); \
else \
echo "Doxygen not installed and prebuilt documents not found"; \
fi
uninstall-local: uninstall-api-docs
uninstall-api-docs:
find "$(DESTDIR)$(htmldir)" -type d -exec chmod 755 {} \;
rm -rf "$(DESTDIR)$(htmldir)"
clean-local: $(CLEAN_TARGETS)
PHONY_TARGETS += clean-all
clean-all:
GIT="$(GIT)" $(SH) $(srcdir)/admin/clean-all.sh
dist-hook: $(DIST_HOOKS)
.PHONY: ${PHONY_TARGETS}

68
contrib/lutok/NEWS Normal file
View file

@ -0,0 +1,68 @@
Changes in version 0.4
======================
Released on 2013/12/07.
* Cope with the lack of AM_PROG_AR in configure.ac, which first
appeared in Automake 1.11.2. Fixes a problem in Ubuntu 10.04
LTS, which appears stuck in 1.11.1.
* Stopped shipping an Atffile. The only supported way to run the tests
is via Kyua.
Interface changes:
* Issue 5: New methods added to the state class: open_all.
* Removed default parameter values from all state methods and all
standalone operations. It is often unclear what the default value is
given that it depends on the specific Lua operation. Being explicit
on the caller side is clearer.
* Modified operations do_file and do_string to support passing a number
of arguments to the loaded chunks and an error handler to the backing
pcall call.
Changes in version 0.3
======================
Released on 2013/06/14.
* Issue 1: Added support for Lua 5.2 while maintaining support for Lua
5.1. Applications using Lutok can be modified to use the new
interface in this new version and thus support both Lua releases.
However, because of incompatible changes to the Lua API, this release
of Lutok is incompatible with previous releases as well.
* Issue 3: Tweaked configure to look for Lua using the pkg-config names
lua-5.2 and lua-5.1. These are the names used by FreeBSD.
Interface changes:
* New global constants: registry_index.
* New methods added to the state class: get_global_table.
* Removed global constants: globals_index.
Changes in version 0.2
======================
Released on 2012/05/30.
* New global constants: globals_index.
* New methods added to the state class: get_metafield, get_metatable,
insert, push_value, raw_get and raw_set.
* Acknowledged that Lua 5.2 is currently not supported.
Changes in version 0.1
======================
Released on 2012/01/29.
* This is the first public release of the Lutok package.

27
contrib/lutok/README Normal file
View file

@ -0,0 +1,27 @@
Lutok is a lightweight C++ API library for Lua.
Lutok provides thin C++ wrappers around the Lua C API to ease the
interaction between C++ and Lua. These wrappers make intensive use of
RAII to prevent resource leakage, expose C++-friendly data types, report
errors by means of exceptions and ensure that the Lua stack is always
left untouched in the face of errors. The library also provides a small
subset of miscellaneous utility functions built on top of the wrappers.
Lutok focuses on providing a clean and safe C++ interface; the drawback
is that it is not suitable for performance-critical environments. In
order to implement error-safe C++ wrappers on top of a Lua C binary
library, Lutok adds several layers or abstraction and error checking
that go against the original spirit of the Lua C API and thus degrade
performance.
For further information on the contents of this distribution file,
please refer to the following other documents:
* AUTHORS: List of authors and contributors to this project.
* COPYING: License information.
* INSTALL: Compilation and installation instructions.
* NEWS: List of major changes between formal releases.
For general project information, please visit:
https://github.com/jmmv/lutok/

8
contrib/lutok/admin/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
ar-lib
compile
config.guess
config.sub
depcomp
install-sh
ltmain.sh
missing

View file

@ -0,0 +1,90 @@
#! /bin/sh
# Copyright 2010 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Prog_Name=${0##*/}
if [ ! -f ./state.hpp ]; then
echo "${Prog_Name}: must be run from the source top directory" 1>&2
exit 1
fi
if [ ! -f configure ]; then
echo "${Prog_Name}: configure not found; nothing to clean?" 1>&2
exit 1
fi
[ -f Makefile ] || ./configure
make distclean
# Top-level directory.
rm -f Makefile.in
rm -f aclocal.m4
rm -rf autom4te.cache
rm -f config.h.in
rm -f configure
rm -f mkinstalldirs
rm -f lutok-*.tar.gz
# admin directory.
rm -f admin/compile
rm -f admin/config.guess
rm -f admin/config.sub
rm -f admin/depcomp
rm -f admin/install-sh
rm -f admin/ltmain.sh
rm -f admin/mdate-sh
rm -f admin/missing
rm -f admin/texinfo.tex
# bootstrap directory.
rm -f bootstrap/package.m4
rm -f bootstrap/testsuite
# doc directory.
rm -f doc/*.info
rm -f doc/stamp-vti
rm -f doc/version.texi
# m4 directory.
rm -f m4/libtool.m4
rm -f m4/lt*.m4
# Files and directories spread all around the tree.
find . -name '#*' | xargs rm -rf
find . -name '*~' | xargs rm -rf
find . -name .deps | xargs rm -rf
find . -name .gdb_history | xargs rm -rf
find . -name .libs | xargs rm -rf
find . -name .tmp | xargs rm -rf
# Show remaining files.
if [ -n "${GIT}" ]; then
echo ">>> untracked and ignored files"
"${GIT}" status --porcelain --ignored | grep -E '^(\?\?|!!)' || true
fi

View file

@ -0,0 +1,51 @@
#! /bin/sh
# Copyright 2014 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -e -x
if [ -d /usr/local/share/aclocal ]; then
autoreconf -isv -I/usr/local/share/aclocal
else
autoreconf -isv
fi
./configure
archflags=
[ "${ARCH?}" != i386 ] || archflags=-m32
f=
f="${f} CPPFLAGS='-I/usr/local/include'"
f="${f} CXX='${CXX} ${archflags}'"
f="${f} LDFLAGS='-L/usr/local/lib -Wl,-R/usr/local/lib'"
f="${f} PKG_CONFIG_PATH='/usr/local/lib/pkgconfig'"
if [ "${AS_ROOT:-no}" = yes ]; then
sudo -H PATH="${PATH}" make distcheck DISTCHECK_CONFIGURE_FLAGS="${f}"
else
make distcheck DISTCHECK_CONFIGURE_FLAGS="${f}"
fi

View file

@ -0,0 +1,109 @@
#! /bin/sh
# Copyright 2014 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -e -x
install_deps() {
sudo apt-get update -qq
local pkgsuffix=
local packages=
if [ "${ARCH?}" = i386 ]; then
pkgsuffix=:i386
packages="${packages} gcc-multilib"
packages="${packages} g++-multilib"
fi
packages="${packages} doxygen"
packages="${packages} gdb"
packages="${packages} liblua5.2-0${pkgsuffix}"
packages="${packages} liblua5.2-dev${pkgsuffix}"
packages="${packages} libsqlite3-0${pkgsuffix}"
packages="${packages} libsqlite3-dev${pkgsuffix}"
packages="${packages} pkg-config${pkgsuffix}"
packages="${packages} sqlite3"
sudo apt-get install -y ${packages}
}
install_from_github() {
local project="${1}"; shift
local name="${1}"; shift
local release="${1}"; shift
local distname="${name}-${release}"
local baseurl="https://github.com/jmmv/${project}"
wget --no-check-certificate \
"${baseurl}/releases/download/${distname}/${distname}.tar.gz"
tar -xzvf "${distname}.tar.gz"
local archflags=
[ "${ARCH?}" != i386 ] || archflags=-m32
cd "${distname}"
./configure \
--disable-developer \
--without-atf \
--without-doxygen \
CFLAGS="${archflags}" \
CPPFLAGS="-I/usr/local/include" \
CXXFLAGS="${archflags}" \
LDFLAGS="-L/usr/local/lib -Wl,-R/usr/local/lib" \
PKG_CONFIG_PATH="/usr/local/lib/pkgconfig"
make
sudo make install
cd -
rm -rf "${distname}" "${distname}.tar.gz"
}
install_from_bintray() {
case "${ARCH?}" in
amd64)
name="20160204-usr-local-kyua-ubuntu-12-04-amd64-${CC?}.tar.gz"
;;
i386)
name="20160714-usr-local-kyua-ubuntu-12-04-i386-${CC?}.tar.gz"
;;
*)
echo "ERROR: Unknown ARCH value ${ARCH}" 1>&2
exit 1
;;
esac
wget "http://dl.bintray.com/jmmv/kyua/${name}" || return 1
sudo tar -xzvp -C / -f "${name}"
rm -f "${name}"
}
install_deps
if ! install_from_bintray; then
install_from_github atf atf 0.20
install_from_github lutok lutok 0.4
install_from_github kyua kyua-testers 0.2
install_from_github kyua kyua-cli 0.8
fi

76
contrib/lutok/c_gate.cpp Normal file
View file

@ -0,0 +1,76 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "c_gate.hpp"
#include "state.ipp"
/// Creates a new gateway to an existing C++ Lua state.
///
/// \param state_ The state to connect to. This object must remain alive while
/// the newly-constructed state_c_gate is alive.
lutok::state_c_gate::state_c_gate(state& state_) :
_state(state_)
{
}
/// Destructor.
///
/// Destroying this object has no implications on the life cycle of the Lua
/// state. Only the corresponding state object controls when the Lua state is
/// closed.
lutok::state_c_gate::~state_c_gate(void)
{
}
/// Creates a C++ state for a C Lua state.
///
/// \warning The created state object does NOT own the C state. You must take
/// care to properly destroy the input lua_State when you are done with it to
/// not leak resources.
///
/// \param raw_state The raw state to wrap temporarily.
///
/// \return The wrapped state without strong ownership on the input state.
lutok::state
lutok::state_c_gate::connect(lua_State* raw_state)
{
return state(static_cast< void* >(raw_state));
}
/// Returns the C native Lua state.
///
/// \return A native lua_State object holding the Lua C API state.
lua_State*
lutok::state_c_gate::c_state(void)
{
return static_cast< lua_State* >(_state.raw_state());
}

71
contrib/lutok/c_gate.hpp Normal file
View file

@ -0,0 +1,71 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file c_gate.hpp
/// Provides direct access to the C state of the Lua wrappers.
#if !defined(LUTOK_C_GATE_HPP)
#define LUTOK_C_GATE_HPP
#include <lua.hpp>
namespace lutok {
class state;
/// Gateway to the raw C state of Lua.
///
/// This class provides a mechanism to muck with the internals of the state
/// wrapper class. Client code may wish to do so if Lutok is missing some
/// features of the performance of Lutok in a particular situation is not
/// reasonable.
///
/// \warning The use of this class is discouraged. By using this class, you are
/// entering the world of unsafety. Anything you do through the objects exposed
/// through this class will not be controlled by RAII patterns not validated in
/// any other way, so you can end up corrupting the Lua state and later get
/// crashes on otherwise perfectly-valid C++ code.
class state_c_gate {
/// The C++ state that this class wraps.
state& _state;
public:
state_c_gate(state&);
~state_c_gate(void);
static state connect(lua_State*);
lua_State* c_state(void);
};
} // namespace lutok
#endif // !defined(LUTOK_C_GATE_HPP)

View file

@ -0,0 +1,74 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "c_gate.hpp"
#include <atf-c++.hpp>
#include <lua.hpp>
#include "state.ipp"
#include "test_utils.hpp"
ATF_TEST_CASE_WITHOUT_HEAD(connect);
ATF_TEST_CASE_BODY(connect)
{
lua_State* raw_state = luaL_newstate();
ATF_REQUIRE(raw_state != NULL);
{
lutok::state state = lutok::state_c_gate::connect(raw_state);
lua_pushinteger(raw(state), 123);
}
// If the wrapper object had closed the Lua state, we could very well crash
// here.
ATF_REQUIRE_EQ(123, lua_tointeger(raw_state, -1));
lua_close(raw_state);
}
ATF_TEST_CASE_WITHOUT_HEAD(c_state);
ATF_TEST_CASE_BODY(c_state)
{
lutok::state state;
state.push_integer(5);
{
lutok::state_c_gate gate(state);
lua_State* raw_state = gate.c_state();
ATF_REQUIRE_EQ(5, lua_tointeger(raw_state, -1));
}
state.pop(1);
}
ATF_INIT_TEST_CASES(tcs)
{
ATF_ADD_TEST_CASE(tcs, c_state);
ATF_ADD_TEST_CASE(tcs, connect);
}

View file

@ -0,0 +1,70 @@
dnl Copyright 2011 Google Inc.
dnl All rights reserved.
dnl
dnl Redistribution and use in source and binary forms, with or without
dnl modification, are permitted provided that the following conditions are
dnl met:
dnl
dnl * Redistributions of source code must retain the above copyright
dnl notice, this list of conditions and the following disclaimer.
dnl * Redistributions in binary form must reproduce the above copyright
dnl notice, this list of conditions and the following disclaimer in the
dnl documentation and/or other materials provided with the distribution.
dnl * Neither the name of Google Inc. nor the names of its contributors
dnl may be used to endorse or promote products derived from this software
dnl without specific prior written permission.
dnl
dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
AC_INIT([Lutok], [0.4],
[lutok-discuss@googlegroups.com], [lutok],
[https://github.com/jmmv/lutok/])
AC_PREREQ([2.65])
AC_COPYRIGHT([Copyright 2011 Google Inc.])
AC_CONFIG_AUX_DIR([admin])
AC_CONFIG_FILES([Doxyfile Makefile])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([state.hpp])
AM_INIT_AUTOMAKE([1.9 check-news foreign subdir-objects -Wall])
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
LT_INIT
AC_LANG([C++])
AC_PROG_CXX
KYUA_REQUIRE_CXX
KYUA_DEVELOPER_MODE([C++])
ATF_CHECK_CXX([>= 0.15])
ATF_CHECK_SH([>= 0.15])
ATF_ARG_WITH
KYUA_DOXYGEN
KYUA_LUA
AC_PATH_PROG([KYUA], [kyua])
AM_CONDITIONAL([HAVE_KYUA], [test -n "${KYUA}"])
AC_PATH_PROG([GIT], [git])
AC_SUBST(pkgconfigdir, \${libdir}/pkgconfig)
AC_SUBST(testsdir, \${exec_prefix}/tests/lutok)
AC_OUTPUT

192
contrib/lutok/debug.cpp Normal file
View file

@ -0,0 +1,192 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cassert>
#include <lua.hpp>
#include <lutok/c_gate.hpp>
#include <lutok/debug.hpp>
#include <lutok/exceptions.hpp>
#include <lutok/state.ipp>
/// Internal implementation for lutok::debug.
struct lutok::debug::impl {
/// The Lua internal debug state.
lua_Debug lua_debug;
};
/// Constructor for an empty debug structure.
lutok::debug::debug(void) :
_pimpl(new impl())
{
}
/// Destructor.
lutok::debug::~debug(void)
{
}
/// Wrapper around lua_getinfo.
///
/// \param s The Lua state.
/// \param what_ The second parameter to lua_getinfo.
///
/// \warning Terminates execution if there is not enough memory to manipulate
/// the Lua stack.
void
lutok::debug::get_info(state& s, const std::string& what_)
{
lua_State* raw_state = state_c_gate(s).c_state();
if (lua_getinfo(raw_state, what_.c_str(), &_pimpl->lua_debug) == 0)
throw lutok::api_error::from_stack(s, "lua_getinfo");
}
/// Wrapper around lua_getstack.
///
/// \param s The Lua state.
/// \param level The second parameter to lua_getstack.
void
lutok::debug::get_stack(state& s, const int level)
{
lua_State* raw_state = state_c_gate(s).c_state();
lua_getstack(raw_state, level, &_pimpl->lua_debug);
}
/// Accessor for the 'event' field of lua_Debug.
///
/// \return Returns the 'event' field of the internal lua_Debug structure.
int
lutok::debug::event(void) const
{
return _pimpl->lua_debug.event;
}
/// Accessor for the 'name' field of lua_Debug.
///
/// \return Returns the 'name' field of the internal lua_Debug structure.
std::string
lutok::debug::name(void) const
{
assert(_pimpl->lua_debug.name != NULL);
return _pimpl->lua_debug.name;
}
/// Accessor for the 'namewhat' field of lua_Debug.
///
/// \return Returns the 'namewhat' field of the internal lua_Debug structure.
std::string
lutok::debug::name_what(void) const
{
assert(_pimpl->lua_debug.namewhat != NULL);
return _pimpl->lua_debug.namewhat;
}
/// Accessor for the 'what' field of lua_Debug.
///
/// \return Returns the 'what' field of the internal lua_Debug structure.
std::string
lutok::debug::what(void) const
{
assert(_pimpl->lua_debug.what != NULL);
return _pimpl->lua_debug.what;
}
/// Accessor for the 'source' field of lua_Debug.
///
/// \return Returns the 'source' field of the internal lua_Debug structure.
std::string
lutok::debug::source(void) const
{
assert(_pimpl->lua_debug.source != NULL);
return _pimpl->lua_debug.source;
}
/// Accessor for the 'currentline' field of lua_Debug.
///
/// \return Returns the 'currentline' field of the internal lua_Debug structure.
int
lutok::debug::current_line(void) const
{
return _pimpl->lua_debug.currentline;
}
/// Accessor for the 'nups' field of lua_Debug.
///
/// \return Returns the 'nups' field of the internal lua_Debug structure.
int
lutok::debug::n_ups(void) const
{
return _pimpl->lua_debug.nups;
}
/// Accessor for the 'linedefined' field of lua_Debug.
///
/// \return Returns the 'linedefined' field of the internal lua_Debug structure.
int
lutok::debug::line_defined(void) const
{
return _pimpl->lua_debug.linedefined;
}
/// Accessor for the 'lastlinedefined' field of lua_Debug.
///
/// \return Returns the 'lastlinedefined' field of the internal lua_Debug
/// structure.
int
lutok::debug::last_line_defined(void) const
{
return _pimpl->lua_debug.lastlinedefined;
}
/// Accessor for the 'short_src' field of lua_Debug.
///
/// \return Returns the 'short_src' field of the internal lua_Debug structure.
std::string
lutok::debug::short_src(void) const
{
assert(_pimpl->lua_debug.short_src != NULL);
return _pimpl->lua_debug.short_src;
}

90
contrib/lutok/debug.hpp Normal file
View file

@ -0,0 +1,90 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file debug.hpp
/// Provides the debug wrapper class for the Lua C debug state.
#if !defined(LUTOK_DEBUG_HPP)
#define LUTOK_DEBUG_HPP
#include <string>
#if defined(_LIBCPP_VERSION) || __cplusplus >= 201103L
#include <memory>
#else
#include <tr1/memory>
#endif
namespace lutok {
class state;
/// A model for the Lua debug state.
///
/// This extremely-simple class provides a mechanism to hide the internals of
/// the C native lua_Debug type, exposing its internal fields using friendlier
/// C++ types.
///
/// This class also acts as a complement to the state class by exposing any
/// state-related functions as methods of this function. For example, while it
/// might seem that get_info() belongs in state, we expose it from here because
/// its result is really mutating a debug object, not the state object.
class debug {
struct impl;
/// Pointer to the shared internal implementation.
#if defined(_LIBCPP_VERSION) || __cplusplus >= 201103L
std::shared_ptr< impl > _pimpl;
#else
std::tr1::shared_ptr< impl > _pimpl;
#endif
public:
debug(void);
~debug(void);
void get_info(state&, const std::string&);
void get_stack(state&, const int);
int event(void) const;
std::string name(void) const;
std::string name_what(void) const;
std::string what(void) const;
std::string source(void) const;
int current_line(void) const;
int n_ups(void) const;
int line_defined(void) const;
int last_line_defined(void) const;
std::string short_src(void) const;
};
} // namespace lutok
#endif // !defined(LUTOK_DEBUG_HPP)

View file

@ -0,0 +1,68 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "debug.hpp"
#include <atf-c++.hpp>
#include <lua.hpp>
#include "state.ipp"
#include "test_utils.hpp"
ATF_TEST_CASE_WITHOUT_HEAD(get_info);
ATF_TEST_CASE_BODY(get_info)
{
lutok::state state;
ATF_REQUIRE(luaL_dostring(raw(state), "\n\nfunction hello() end\n"
"return hello") == 0);
lutok::debug debug;
debug.get_info(state, ">S");
ATF_REQUIRE_EQ(3, debug.line_defined());
}
ATF_TEST_CASE_WITHOUT_HEAD(get_stack);
ATF_TEST_CASE_BODY(get_stack)
{
lutok::state state;
ATF_REQUIRE(luaL_dostring(raw(state), "error('Hello')") == 1);
lutok::debug debug;
debug.get_stack(state, 0);
lua_pop(raw(state), 1);
// Not sure if we can actually validate anything here, other than we did not
// crash... (because get_stack only is supposed to update internal values of
// the debug structure).
}
ATF_INIT_TEST_CASES(tcs)
{
ATF_ADD_TEST_CASE(tcs, get_info);
ATF_ADD_TEST_CASE(tcs, get_stack);
}

View file

@ -0,0 +1,67 @@
# Copyright 2012 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
CXX ?= c++
CPPFLAGS ?=
CXXFLAGS ?= -Wall -O2
LDFLAGS ?=
LIBS ?=
LUTOK_CPPFLAGS = $$(pkg-config --cflags-only-I lutok)
LUTOK_CXXFLAGS = $$(pkg-config --cflags-only-other lutok)
LUTOK_LDFLAGS = $$(pkg-config --libs-only-L lutok) \
$$(pkg-config --libs-only-other lutok)
LUTOK_LIBS = $$(pkg-config --libs-only-l lutok)
BUILD = $(CXX) \
$(CPPFLAGS) $(LUTOK_CPPFLAGS) \
$(CXXFLAGS) $(LUTOK_CXXFLAGS) \
$(LDFLAGS) $(LUTOK_LDFLAGS) \
-o $${target} $${source} \
$(LIBS) $(LUTOK_LIBS)
PROGRAMS = bindings hello interpreter raii
.PHONY: all
all: $(PROGRAMS)
bindings: bindings.cpp
@target=bindings source=bindings.cpp; echo $(BUILD); $(BUILD)
hello: hello.cpp
@target=hello source=hello.cpp; echo $(BUILD); $(BUILD)
interpreter: interpreter.cpp
@target=interpreter source=interpreter.cpp; echo $(BUILD); $(BUILD)
raii: raii.cpp
@target=raii source=raii.cpp; echo $(BUILD); $(BUILD)
.PHONY: clean
clean:
rm -f $(PROGRAMS)

View file

@ -0,0 +1,133 @@
// Copyright 2012 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file examples/bindings.cpp
/// Showcases how to define Lua functions from C++ code.
///
/// A major selling point of Lua is that it is very easy too hook native C and
/// C++ functions into the runtime environment so that Lua can call them. The
/// purpose of this example program is to show how this is done by using Lutok.
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <map>
#include <sstream>
#include <stdexcept>
#include <string>
#include <lutok/exceptions.hpp>
#include <lutok/operations.hpp>
#include <lutok/state.ipp>
/// Calculates the factorial of a given number.
///
/// \param i The postivie number to calculate the factorial of.
///
/// \return The factorial of i.
static int
factorial(const int i)
{
assert(i >= 0);
if (i == 0)
return 1;
else
return i * factorial(i - 1);
}
/// A custom factorial function for Lua.
///
/// \pre stack(-1) contains the number to calculate the factorial of.
/// \post stack(-1) contains the result of the operation.
///
/// \param state The Lua state from which to get the function arguments and into
/// which to push the results.
///
/// \return The number of results pushed onto the stack, i.e. 1.
///
/// \throw std::runtime_error If the input parameters are invalid. Note that
/// Lutok will convert this exception to lutok::error.
static int
lua_factorial(lutok::state& state)
{
if (!state.is_number(-1))
throw std::runtime_error("Argument to factorial must be an integer");
const int i = state.to_integer(-1);
if (i < 0)
throw std::runtime_error("Argument to factorial must be positive");
state.push_integer(factorial(i));
return 1;
}
/// Program's entry point.
///
/// \param argc Length of argv. Must be 2.
/// \param argv Command-line arguments to the program. The first argument to
/// the tool has to be a number.
///
/// \return A system exit code.
int
main(int argc, char** argv)
{
if (argc != 2) {
std::cerr << "Usage: bindings <number>\n";
return EXIT_FAILURE;
}
// Create a new Lua session and load the print() function.
lutok::state state;
state.open_base();
// Construct a 'module' that contains an entry point to our native factorial
// function. A module is just a Lua table that contains a mapping of names
// to functions. Instead of creating a module by using our create_module()
// helper function, we could have used push_cxx_function on the state to
// define the function ourselves.
std::map< std::string, lutok::cxx_function > module;
module["factorial"] = lua_factorial;
lutok::create_module(state, "native", module);
// Use a little Lua script to call our native factorial function providing
// it the first argument passed to the program. Note that this will error
// out in a controlled manner if the passed argument is not an integer. The
// important thing to notice is that the exception comes from our own C++
// binding and that it has been converted to a lutok::error.
std::ostringstream script;
script << "print(native.factorial(" << argv[1] << "))";
try {
lutok::do_string(state, script.str(), 0, 0, 0);
return EXIT_SUCCESS;
} catch (const lutok::error& e) {
std::cerr << "ERROR: " << e.what() << '\n';
return EXIT_FAILURE;
}
}

View file

@ -0,0 +1,58 @@
// Copyright 2012 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file examples/hello.cpp
/// Minimal example using Lua code to print a traditional hello-world message.
#include <cstdlib>
#include <lutok/state.ipp>
/// Program's entry point.
///
/// \return A system exit code.
int
main(void)
{
// Initializes a new Lua state. Every Lua state is independent from each
// other.
lutok::state state;
// Loads the standard library into the Lua state. Among many other
// functions, this gives us access to print(), which is used below.
state.open_base();
// Pushes the print() function into the Lua stack, then its only argument,
// and then proceeds to execute it within the Lua state.
state.get_global("print");
state.push_string("Hello, world!");
state.pcall(1, 0, 0);
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,83 @@
// Copyright 2012 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file examples/interpreter.cpp
/// Implementation of a basic command-line Lua interpreter.
#include <cstdlib>
#include <iostream>
#include <string>
#include <lutok/exceptions.hpp>
#include <lutok/operations.hpp>
#include <lutok/state.ipp>
/// Executes a Lua statement provided by the user with error checking.
///
/// \param state The Lua state in which to process the statement.
/// \param line The textual statement provided by the user.
static void
run_statement(lutok::state& state, const std::string& line)
{
try {
// This utility function allows us to feed a given piece of Lua code to
// the interpreter and process it. The piece of code can include
// multiple statements separated by a semicolon or by a newline
// character.
lutok::do_string(state, line, 0, 0, 0);
} catch (const lutok::error& error) {
std::cerr << "ERROR: " << error.what() << '\n';
}
}
/// Program's entry point.
///
/// \return A system exit code.
int
main(void)
{
// Create a new session and load some standard libraries.
lutok::state state;
state.open_base();
state.open_string();
state.open_table();
for (;;) {
std::cout << "lua> ";
std::cout.flush();
std::string line;
if (!std::getline(std::cin, line).good())
break;
run_statement(state, line);
}
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,126 @@
// Copyright 2012 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file examples/raii.cpp
/// Demonstrates how RAII helps in keeping the Lua state consistent.
///
/// One of the major complains that is raised against the Lua C API is that it
/// is very hard to ensure it remains consistent during the execution of the
/// program. In the case of native C code, there exist many tools that help the
/// developer catch memory leaks, access to uninitialized variables, etc.
/// However, when using the Lua C API, none of these tools can validate that,
/// for example, the Lua stack remains balanced across calls.
///
/// Enter RAII. The RAII pattern, intensively applied by Lutok, helps the
/// developer in maintaining the Lua state consistent at all times in a
/// transparent manner. This example program attempts to illustrate this.
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <string>
#include <lutok/operations.hpp>
#include <lutok/stack_cleaner.hpp>
#include <lutok/state.ipp>
/// Prints the string-typed field of a table.
///
/// If the field contains a string, this function prints its value. If the
/// field contains any other type, this prints an error message.
///
/// \pre The top of the Lua stack in 'state' references a table.
///
/// \param state The Lua state.
/// \param field The name of the string-typed field.
static void
print_table_field(lutok::state& state, const std::string& field)
{
assert(state.is_table(-1));
// Bring in some RAII magic: the stack_cleaner object captures the current
// height of the Lua stack at this point. Whenever the object goes out of
// scope, it will pop as many entries from the stack as necessary to restore
// the stack to its previous level.
//
// This ensures that, no matter how we exit the function, we do not leak
// objects in the stack.
lutok::stack_cleaner cleaner(state);
// Stack contents: -1: table.
state.push_string(field);
// Stack contents: -2: table, -1: field name.
state.get_table(-2);
// Stack contents: -2: table, -1: field value.
if (!state.is_string(-1)) {
std::cout << "The field " << field << " does not contain a string\n";
// Stack contents: -2: table, -1: field value.
//
// This is different than when we started! We should pop our extra
// value from the stack at this point. However, it is extremely common
// for software to have bugs (in this case, leaks) in error paths,
// mostly because such code paths are rarely exercised.
//
// By using the stack_cleaner object, we can be confident that the Lua
// stack will be cleared for us at this point, no matter what happened
// earlier on the stack nor how we exit the function.
return;
}
std::cout << "String in field " << field << ": " << state.to_string(-1)
<< '\n';
// A well-behaved program explicitly pops anything extra from the stack to
// return it to its original state. Mostly for clarity.
state.pop(1);
// Stack contents: -1: table. Same as when we started.
}
/// Program's entry point.
///
/// \return A system exit code.
int
main(void)
{
lutok::state state;
state.open_base();
lutok::do_string(state, "example = {foo='hello', bar=123, baz='bye'}",
0, 0, 0);
state.get_global("example");
print_table_field(state, "foo");
print_table_field(state, "bar");
print_table_field(state, "baz");
state.pop(1);
return EXIT_SUCCESS;
}

115
contrib/lutok/examples_test.sh Executable file
View file

@ -0,0 +1,115 @@
#! __ATF_SH__
# Copyright 2012 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Cxx="__CXX__"
ExamplesDir="__EXAMPLESDIR__"
LibDir="__LIBDIR__"
make_example() {
cp "${ExamplesDir}/Makefile" "${ExamplesDir}/${1}.cpp" .
make CXX="${Cxx}" "${1}"
# Ensure that the binary we just built can find liblutok. This is
# needed because the lutok.pc file (which the Makefile used above
# queries) does not provide rpaths to the installed library and
# therefore the binary may not be able to locate it. Hardcoding the
# rpath flags into lutok.pc is non-trivial because we simply don't
# have any knowledge about what the correct flag to set an rpath is.
#
# Additionally, setting rpaths is not always the right thing to do.
# For example, pkgsrc will automatically change lutok.pc to add the
# missing rpath, in which case this is unnecessary. But in the case
# of Fedora, adding rpaths goes against the packaging guidelines.
if [ -n "${LD_LIBRARY_PATH}" ]; then
export LD_LIBRARY_PATH="${LibDir}:${LD_LIBRARY_PATH}"
else
export LD_LIBRARY_PATH="${LibDir}"
fi
}
example_test_case() {
local name="${1}"; shift
atf_test_case "${name}"
eval "${name}_head() { \
atf_set 'require.files' '${ExamplesDir}/${name}.cpp'; \
atf_set 'require.progs' 'make pkg-config'; \
}"
eval "${name}_body() { \
make_example '${name}'; \
${name}_validate; \
}"
}
example_test_case bindings
bindings_validate() {
atf_check -s exit:0 -o inline:'120\n' ./bindings 5
atf_check -s exit:1 -e match:'Argument.*must be an integer' ./bindings foo
atf_check -s exit:1 -e match:'Argument.*must be positive' ./bindings -5
}
example_test_case hello
hello_validate() {
atf_check -s exit:0 -o inline:'Hello, world!\n' ./hello
}
example_test_case interpreter
interpreter_validate() {
cat >script.lua <<EOF
test_variable = 12345
print("From the interpreter: " .. (test_variable - 345))
EOF
atf_check -s exit:0 -o match:"From the interpreter: 12000" \
-x "./interpreter <script.lua"
}
example_test_case raii
raii_validate() {
cat >expout <<EOF
String in field foo: hello
String in field bar: 123
String in field baz: bye
EOF
atf_check -s exit:0 -o file:expout ./raii
}
atf_init_test_cases() {
atf_add_test_case bindings
atf_add_test_case hello
atf_add_test_case interpreter
atf_add_test_case raii
}

View file

@ -0,0 +1,126 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cassert>
#include <lua.hpp>
#include "c_gate.hpp"
#include "exceptions.hpp"
#include "state.ipp"
/// Constructs a new error with a plain-text message.
///
/// \param message The plain-text error message.
lutok::error::error(const std::string& message) :
std::runtime_error(message)
{
}
/// Destructor for the error.
lutok::error::~error(void) throw()
{
}
/// Constructs a new error.
///
/// \param api_function_ The name of the API function that caused the error.
/// \param message The plain-text error message provided by Lua.
lutok::api_error::api_error(const std::string& api_function_,
const std::string& message) :
error(message),
_api_function(api_function_)
{
}
/// Destructor for the error.
lutok::api_error::~api_error(void) throw()
{
}
/// Constructs a new api_error with the message on the top of the Lua stack.
///
/// \pre There is an error message on the top of the stack.
/// \post The error message is popped from the stack.
///
/// \param state_ The Lua state.
/// \param api_function_ The name of the Lua API function that caused the error.
///
/// \return A new api_error with the popped message.
lutok::api_error
lutok::api_error::from_stack(state& state_, const std::string& api_function_)
{
lua_State* raw_state = lutok::state_c_gate(state_).c_state();
assert(lua_isstring(raw_state, -1));
const std::string message = lua_tostring(raw_state, -1);
lua_pop(raw_state, 1);
return lutok::api_error(api_function_, message);
}
/// Gets the name of the Lua API function that caused this error.
///
/// \return The name of the function.
const std::string&
lutok::api_error::api_function(void) const
{
return _api_function;
}
/// Constructs a new error.
///
/// \param filename_ The file that count not be found.
lutok::file_not_found_error::file_not_found_error(
const std::string& filename_) :
error("File '" + filename_ + "' not found"),
_filename(filename_)
{
}
/// Destructor for the error.
lutok::file_not_found_error::~file_not_found_error(void) throw()
{
}
/// Gets the name of the file that could not be found.
///
/// \return The name of the file.
const std::string&
lutok::file_not_found_error::filename(void) const
{
return _filename;
}

View file

@ -0,0 +1,83 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file exceptions.hpp
/// Exception types raised by lutok.
#if !defined(LUTOK_EXCEPTIONS_HPP)
#define LUTOK_EXCEPTIONS_HPP
#include <stdexcept>
#include <string>
namespace lutok {
class state;
/// Base exception for lua errors.
class error : public std::runtime_error {
public:
explicit error(const std::string&);
virtual ~error(void) throw();
};
/// Exception for errors raised by the Lua API library.
class api_error : public error {
/// Name of the Lua C API function that caused the error.
std::string _api_function;
public:
explicit api_error(const std::string&, const std::string&);
virtual ~api_error(void) throw();
static api_error from_stack(state&, const std::string&);
const std::string& api_function(void) const;
};
/// File not found error.
class file_not_found_error : public error {
/// Name of the not-found file.
std::string _filename;
public:
explicit file_not_found_error(const std::string&);
virtual ~file_not_found_error(void) throw();
const std::string& filename(void) const;
};
} // namespace lutok
#endif // !defined(LUTOK_EXCEPTIONS_HPP)

View file

@ -0,0 +1,88 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "exceptions.hpp"
#include <cstring>
#include <atf-c++.hpp>
#include "state.ipp"
ATF_TEST_CASE_WITHOUT_HEAD(error);
ATF_TEST_CASE_BODY(error)
{
const lutok::error e("Some text");
ATF_REQUIRE(std::strcmp("Some text", e.what()) == 0);
}
ATF_TEST_CASE_WITHOUT_HEAD(api_error__explicit);
ATF_TEST_CASE_BODY(api_error__explicit)
{
const lutok::api_error e("some_function", "Some text");
ATF_REQUIRE(std::strcmp("Some text", e.what()) == 0);
ATF_REQUIRE_EQ("some_function", e.api_function());
}
ATF_TEST_CASE_WITHOUT_HEAD(api_error__from_stack);
ATF_TEST_CASE_BODY(api_error__from_stack)
{
lutok::state state;
state.push_integer(123);
state.push_string("The error message");
const lutok::api_error e = lutok::api_error::from_stack(state,
"the_function");
ATF_REQUIRE_EQ(1, state.get_top());
ATF_REQUIRE_EQ(123, state.to_integer(-1));
state.pop(1);
ATF_REQUIRE(std::strcmp("The error message", e.what()) == 0);
ATF_REQUIRE_EQ("the_function", e.api_function());
}
ATF_TEST_CASE_WITHOUT_HEAD(file_not_found_error);
ATF_TEST_CASE_BODY(file_not_found_error)
{
const lutok::file_not_found_error e("missing-file");
ATF_REQUIRE(std::strcmp("File 'missing-file' not found", e.what()) == 0);
ATF_REQUIRE_EQ("missing-file", e.filename());
}
ATF_INIT_TEST_CASES(tcs)
{
ATF_ADD_TEST_CASE(tcs, error);
ATF_ADD_TEST_CASE(tcs, api_error__explicit);
ATF_ADD_TEST_CASE(tcs, api_error__from_stack);
ATF_ADD_TEST_CASE(tcs, file_not_found_error);
}

View file

@ -0,0 +1,4 @@
This directory contains forward includes for the public header files of
Lutok. These files are only necessary during the build of Lutok itself
so that the compiler can locate the include files in a path that mimics
the final installation location.

View file

@ -0,0 +1 @@
#include "../../c_gate.hpp"

View file

@ -0,0 +1 @@
#include "../../debug.hpp"

View file

@ -0,0 +1 @@
#include "../../exceptions.hpp"

View file

@ -0,0 +1 @@
#include "../../operations.hpp"

View file

@ -0,0 +1 @@
#include "../../stack_cleaner.hpp"

View file

@ -0,0 +1 @@
#include "../../state.hpp"

View file

@ -0,0 +1 @@
#include "../../state.ipp"

View file

@ -0,0 +1,8 @@
includedir=__INCLUDEDIR__
libdir=__LIBDIR__
Name: lutok
Description: Lightweight C++ API for Lua
Version: __VERSION__
Cflags: __LUA_CFLAGS__ -I${includedir}
Libs: __LUA_LIBS__ -L${libdir} -llutok

5
contrib/lutok/m4/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
libtool.m4
ltoptions.m4
ltsugar.m4
ltversion.m4
lt~obsolete.m4

View file

@ -0,0 +1,100 @@
dnl Copyright 2010 Google Inc.
dnl All rights reserved.
dnl
dnl Redistribution and use in source and binary forms, with or without
dnl modification, are permitted provided that the following conditions are
dnl met:
dnl
dnl * Redistributions of source code must retain the above copyright
dnl notice, this list of conditions and the following disclaimer.
dnl * Redistributions in binary form must reproduce the above copyright
dnl notice, this list of conditions and the following disclaimer in the
dnl documentation and/or other materials provided with the distribution.
dnl * Neither the name of Google Inc. nor the names of its contributors
dnl may be used to endorse or promote products derived from this software
dnl without specific prior written permission.
dnl
dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dnl
dnl KYUA_REQUIRE_CXX
dnl
dnl Ensures the C++ compiler detected by AC_PROG_CXX is present and works.
dnl The compiler check should be performed here, but it seems like Autoconf
dnl does not allow it.
dnl
AC_DEFUN([KYUA_REQUIRE_CXX], [
AC_CACHE_CHECK([whether the C++ compiler works],
[atf_cv_prog_cxx_works],
[AC_LANG_PUSH([C++])
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])],
[atf_cv_prog_cxx_works=yes],
[atf_cv_prog_cxx_works=no])
AC_LANG_POP([C++])])
if test "${atf_cv_prog_cxx_works}" = no; then
AC_MSG_ERROR([C++ compiler cannot create executables])
fi
])
dnl
dnl KYUA_ATTRIBUTE_NORETURN
dnl
dnl Checks if the current compiler has a way to mark functions that do not
dnl return and defines ATTRIBUTE_NORETURN to the appropriate string.
dnl
AC_DEFUN([KYUA_ATTRIBUTE_NORETURN], [
dnl This check is overly simple and should be fixed. For example,
dnl Sun's cc does support the noreturn attribute but CC (the C++
dnl compiler) does not. And in that case, CC just raises a warning
dnl during compilation, not an error.
AC_MSG_CHECKING(whether __attribute__((noreturn)) is supported)
AC_RUN_IFELSE([AC_LANG_PROGRAM([], [
#if ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
return 0;
#else
return 1;
#endif
])],
[AC_MSG_RESULT(yes)
value="__attribute__((noreturn))"],
[AC_MSG_RESULT(no)
value=""]
)
AC_SUBST([ATTRIBUTE_NORETURN], [${value}])
])
dnl
dnl KYUA_ATTRIBUTE_UNUSED
dnl
dnl Checks if the current compiler has a way to mark parameters as unused
dnl so that the -Wunused-parameter warning can be avoided.
dnl
AC_DEFUN([KYUA_ATTRIBUTE_UNUSED], [
AC_MSG_CHECKING(whether __attribute__((__unused__)) is supported)
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([
static void
function(int a __attribute__((__unused__)))
{
}], [
function(3);
return 0;
])],
[AC_MSG_RESULT(yes)
value="__attribute__((__unused__))"],
[AC_MSG_RESULT(no)
value=""]
)
AC_SUBST([ATTRIBUTE_UNUSED], [${value}])
])

View file

@ -0,0 +1,159 @@
dnl Copyright 2010 Google Inc.
dnl All rights reserved.
dnl
dnl Redistribution and use in source and binary forms, with or without
dnl modification, are permitted provided that the following conditions are
dnl met:
dnl
dnl * Redistributions of source code must retain the above copyright
dnl notice, this list of conditions and the following disclaimer.
dnl * Redistributions in binary form must reproduce the above copyright
dnl notice, this list of conditions and the following disclaimer in the
dnl documentation and/or other materials provided with the distribution.
dnl * Neither the name of Google Inc. nor the names of its contributors
dnl may be used to endorse or promote products derived from this software
dnl without specific prior written permission.
dnl
dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dnl \file compiler-flags.m4
dnl
dnl Macros to check for the existence of compiler flags. The macros in this
dnl file support both C and C++.
dnl
dnl Be aware that, in order to detect a flag accurately, we may need to enable
dnl strict warning checking in the compiler (i.e. enable -Werror). Some
dnl compilers, e.g. Clang, report unknown -W flags as warnings unless -Werror is
dnl selected. This fact would confuse the flag checks below because we would
dnl conclude that a flag is valid while in reality it is not. To resolve this,
dnl the macros below will pass -Werror to the compiler along with any other flag
dnl being checked.
dnl Checks for a compiler flag and sets a result variable.
dnl
dnl This is an auxiliary macro for the implementation of _KYUA_FLAG.
dnl
dnl \param 1 The shell variable containing the compiler name. Used for
dnl reporting purposes only. C or CXX.
dnl \param 2 The shell variable containing the flags for the compiler.
dnl CFLAGS or CXXFLAGS.
dnl \param 3 The name of the compiler flag to check for.
dnl \param 4 The shell variable to set with the result of the test. Will
dnl be set to 'yes' if the flag is valid, 'no' otherwise.
dnl \param 5 Additional, optional flags to pass to the C compiler while
dnl looking for the flag in $3. We use this here to pass -Werror to the
dnl flag checks (unless we are checking for -Werror already).
AC_DEFUN([_KYUA_FLAG_AUX], [
if test x"${$4-unset}" = xunset; then
AC_MSG_CHECKING(whether ${$1} supports $3)
saved_flags="${$2}"
$4=no
$2="${$2} $5 $3"
AC_LINK_IFELSE([AC_LANG_PROGRAM([], [return 0;])],
AC_MSG_RESULT(yes)
$4=yes,
AC_MSG_RESULT(no))
$2="${saved_flags}"
fi
])
dnl Checks for a compiler flag and appends it to a result variable.
dnl
dnl \param 1 The shell variable containing the compiler name. Used for
dnl reporting purposes only. CC or CXX.
dnl \param 2 The shell variable containing the flags for the compiler.
dnl CFLAGS or CXXFLAGS.
dnl \param 3 The name of the compiler flag to check for.
dnl \param 4 The shell variable to which to append $3 if the flag is valid.
AC_DEFUN([_KYUA_FLAG], [
_KYUA_FLAG_AUX([$1], [$2], [-Werror], [kyua_$1_has_werror])
if test "$3" = "-Werror"; then
found=${kyua_$1_has_werror}
else
found=unset
if test ${kyua_$1_has_werror} = yes; then
_KYUA_FLAG_AUX([$1], [$2], [$3], [found], [-Werror])
else
_KYUA_FLAG_AUX([$1], [$2], [$3], [found], [])
fi
fi
if test ${found} = yes; then
$4="${$4} $3"
fi
])
dnl Checks for a C compiler flag and appends it to a variable.
dnl
dnl \pre The current language is C.
dnl
dnl \param 1 The name of the compiler flag to check for.
dnl \param 2 The shell variable to which to append $1 if the flag is valid.
AC_DEFUN([KYUA_CC_FLAG], [
AC_LANG_ASSERT([C])
_KYUA_FLAG([CC], [CFLAGS], [$1], [$2])
])
dnl Checks for a C++ compiler flag and appends it to a variable.
dnl
dnl \pre The current language is C++.
dnl
dnl \param 1 The name of the compiler flag to check for.
dnl \param 2 The shell variable to which to append $1 if the flag is valid.
AC_DEFUN([KYUA_CXX_FLAG], [
AC_LANG_ASSERT([C++])
_KYUA_FLAG([CXX], [CXXFLAGS], [$1], [$2])
])
dnl Checks for a set of C compiler flags and appends them to CFLAGS.
dnl
dnl The checks are performed independently and only when all the checks are
dnl done, the output variable is modified.
dnl
dnl \param 1 Whitespace-separated list of C flags to check.
AC_DEFUN([KYUA_CC_FLAGS], [
AC_LANG_PUSH([C])
valid_cflags=
for f in $1; do
KYUA_CC_FLAG(${f}, valid_cflags)
done
if test -n "${valid_cflags}"; then
CFLAGS="${CFLAGS} ${valid_cflags}"
fi
AC_LANG_POP([C])
])
dnl Checks for a set of C++ compiler flags and appends them to CXXFLAGS.
dnl
dnl The checks are performed independently and only when all the checks are
dnl done, the output variable is modified.
dnl
dnl \pre The current language is C++.
dnl
dnl \param 1 Whitespace-separated list of C flags to check.
AC_DEFUN([KYUA_CXX_FLAGS], [
AC_LANG_PUSH([C++])
valid_cxxflags=
for f in $1; do
KYUA_CXX_FLAG(${f}, valid_cxxflags)
done
if test -n "${valid_cxxflags}"; then
CXXFLAGS="${CXXFLAGS} ${valid_cxxflags}"
fi
AC_LANG_POP([C++])
])

View file

@ -0,0 +1,112 @@
dnl Copyright 2010 Google Inc.
dnl All rights reserved.
dnl
dnl Redistribution and use in source and binary forms, with or without
dnl modification, are permitted provided that the following conditions are
dnl met:
dnl
dnl * Redistributions of source code must retain the above copyright
dnl notice, this list of conditions and the following disclaimer.
dnl * Redistributions in binary form must reproduce the above copyright
dnl notice, this list of conditions and the following disclaimer in the
dnl documentation and/or other materials provided with the distribution.
dnl * Neither the name of Google Inc. nor the names of its contributors
dnl may be used to endorse or promote products derived from this software
dnl without specific prior written permission.
dnl
dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dnl \file developer-mode.m4
dnl
dnl "Developer mode" is a mode in which the build system reports any
dnl build-time warnings as fatal errors. This helps in minimizing the
dnl amount of trivial coding problems introduced in the code.
dnl Unfortunately, this is not bullet-proof due to the wide variety of
dnl compilers available and their different warning diagnostics.
dnl
dnl When developer mode support is added to a package, the compilation will
dnl gain a bunch of extra warning diagnostics. These will NOT be enforced
dnl unless developer mode is enabled.
dnl
dnl Developer mode is enabled when the user requests it through the
dnl configure command line, or when building from the repository. The
dnl latter is to minimize the risk of committing new code with warnings
dnl into the tree.
dnl Adds "developer mode" support to the package.
dnl
dnl This macro performs the actual definition of the --enable-developer
dnl flag and implements all of its logic. See the file-level comment for
dnl details as to what this implies.
AC_DEFUN([KYUA_DEVELOPER_MODE], [
m4_foreach([language], [$1], [m4_set_add([languages], language)])
AC_ARG_ENABLE(
[developer],
AS_HELP_STRING([--enable-developer], [enable developer features]),,
[if test -d ${srcdir}/.git; then
AC_MSG_NOTICE([building from HEAD; developer mode autoenabled])
enable_developer=yes
else
enable_developer=no
fi])
#
# The following warning flags should also be enabled but cannot be.
# Reasons given below.
#
# -Wold-style-cast: Raises errors when using TIOCGWINSZ, at least under
# Mac OS X. This is due to the way _IOR is defined.
#
try_c_cxx_flags="-D_FORTIFY_SOURCE=2 \
-Wall \
-Wcast-qual \
-Wextra \
-Wpointer-arith \
-Wredundant-decls \
-Wreturn-type \
-Wshadow \
-Wsign-compare \
-Wswitch \
-Wwrite-strings"
try_c_flags="-Wmissing-prototypes \
-Wno-traditional \
-Wstrict-prototypes"
try_cxx_flags="-Wabi \
-Wctor-dtor-privacy \
-Wno-deprecated \
-Wno-non-template-friend \
-Wno-pmf-conversions \
-Wnon-virtual-dtor \
-Woverloaded-virtual \
-Wreorder \
-Wsign-promo \
-Wsynth"
if test ${enable_developer} = yes; then
try_werror=yes
try_c_cxx_flags="${try_c_cxx_flags} -g -Werror"
else
try_werror=no
try_c_cxx_flags="${try_c_cxx_flags} -DNDEBUG"
fi
m4_set_contains([languages], [C],
[KYUA_CC_FLAGS(${try_c_cxx_flags} ${try_c_flags})])
m4_set_contains([languages], [C++],
[KYUA_CXX_FLAGS(${try_c_cxx_flags} ${try_cxx_flags})])
])

View file

@ -0,0 +1,62 @@
dnl Copyright 2010 Google Inc.
dnl All rights reserved.
dnl
dnl Redistribution and use in source and binary forms, with or without
dnl modification, are permitted provided that the following conditions are
dnl met:
dnl
dnl * Redistributions of source code must retain the above copyright
dnl notice, this list of conditions and the following disclaimer.
dnl * Redistributions in binary form must reproduce the above copyright
dnl notice, this list of conditions and the following disclaimer in the
dnl documentation and/or other materials provided with the distribution.
dnl * Neither the name of Google Inc. nor the names of its contributors
dnl may be used to endorse or promote products derived from this software
dnl without specific prior written permission.
dnl
dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dnl
dnl KYUA_DOXYGEN
dnl
dnl Adds a --with-doxygen flag to the configure script and, when Doxygen support
dnl is requested by the user, sets DOXYGEN to the path of the Doxygen binary and
dnl enables the WITH_DOXYGEN Automake conditional.
dnl
AC_DEFUN([KYUA_DOXYGEN], [
AC_ARG_WITH([doxygen],
AS_HELP_STRING([--with-doxygen],
[build documentation for internal APIs]),
[],
[with_doxygen=auto])
if test "${with_doxygen}" = yes; then
AC_PATH_PROG([DOXYGEN], [doxygen], [])
if test -z "${DOXYGEN}"; then
AC_MSG_ERROR([Doxygen explicitly requested but not found])
fi
elif test "${with_doxygen}" = auto; then
AC_PATH_PROG([DOXYGEN], [doxygen], [])
elif test "${with_doxygen}" = no; then
DOXYGEN=
else
AC_MSG_CHECKING([for doxygen])
DOXYGEN="${with_doxygen}"
AC_MSG_RESULT([${DOXYGEN}])
if test ! -x "${DOXYGEN}"; then
AC_MSG_ERROR([Doxygen binary ${DOXYGEN} is not executable])
fi
fi
AM_CONDITIONAL([WITH_DOXYGEN], [test -n "${DOXYGEN}"])
AC_SUBST([DOXYGEN])
])

69
contrib/lutok/m4/lua.m4 Normal file
View file

@ -0,0 +1,69 @@
dnl Copyright 2011 Google Inc.
dnl All rights reserved.
dnl
dnl Redistribution and use in source and binary forms, with or without
dnl modification, are permitted provided that the following conditions are
dnl met:
dnl
dnl * Redistributions of source code must retain the above copyright
dnl notice, this list of conditions and the following disclaimer.
dnl * Redistributions in binary form must reproduce the above copyright
dnl notice, this list of conditions and the following disclaimer in the
dnl documentation and/or other materials provided with the distribution.
dnl * Neither the name of Google Inc. nor the names of its contributors
dnl may be used to endorse or promote products derived from this software
dnl without specific prior written permission.
dnl
dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dnl
dnl KYUA_LUA
dnl
dnl Helper macro to detect Lua in a variety of systems.
dnl
AC_DEFUN([KYUA_LUA], [
lua_found=no
for lua_release in 5.2 5.1; do
if test "${lua_found}" = no; then
PKG_CHECK_MODULES([LUA], [lua${lua_release} >= ${lua_release}],
[lua_found=yes], [true])
fi
if test "${lua_found}" = no; then
PKG_CHECK_MODULES([LUA], [lua-${lua_release} >= ${lua_release}],
[lua_found=yes], [true])
fi
if test "${lua_found}" = no; then
PKG_CHECK_MODULES([LUA], [lua >= ${lua_release}],
[lua_found=yes], [true])
fi
test "${lua_found}" = no || break
done
if test "${lua_found}" = no; then
AC_PATH_PROGS([LUA_CONFIG], [lua-config], [unset])
if test "${LUA_CONFIG}" != unset; then
AC_SUBST([LUA_CFLAGS], [$(${LUA_CONFIG} --include)])
AC_SUBST([LUA_LIBS], [$(${LUA_CONFIG} --libs)])
lua_found=yes
fi
fi
if test "${lua_found}" = no; then
AC_MSG_ERROR([lua (5.1 or newer) is required])
else
AC_MSG_NOTICE([using LUA_CFLAGS = ${LUA_CFLAGS}])
AC_MSG_NOTICE([using LUA_LIBS = ${LUA_LIBS}])
fi
])

View file

@ -0,0 +1,153 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cassert>
#include <lua.hpp>
#include "exceptions.hpp"
#include "operations.hpp"
#include "stack_cleaner.hpp"
#include "state.hpp"
/// Creates a module: i.e. a table with a set of methods in it.
///
/// \param s The Lua state.
/// \param name The name of the module to create.
/// \param members The list of member functions to add to the module.
void
lutok::create_module(state& s, const std::string& name,
const std::map< std::string, cxx_function >& members)
{
stack_cleaner cleaner(s);
s.new_table();
for (std::map< std::string, cxx_function >::const_iterator
iter = members.begin(); iter != members.end(); iter++) {
s.push_string((*iter).first);
s.push_cxx_function((*iter).second);
s.set_table(-3);
}
s.set_global(name);
}
/// Loads and processes a Lua file.
///
/// This is a replacement for luaL_dofile but with proper error reporting
/// and stack control.
///
/// \param s The Lua state.
/// \param file The file to load.
/// \param nargs The number of arguments on the stack to pass to the file.
/// \param nresults The number of results to expect; -1 for any.
/// \param errfunc If not 0, index of a function in the stack to act as an
/// error handler.
///
/// \return The number of results left on the stack.
///
/// \throw error If there is a problem processing the file.
unsigned int
lutok::do_file(state& s, const std::string& file, const int nargs,
const int nresults, const int errfunc)
{
assert(nresults >= -1);
const int height = s.get_top() - nargs;
try {
s.load_file(file);
if (nargs > 0)
s.insert(-nargs - 1);
s.pcall(nargs, nresults == -1 ? LUA_MULTRET : nresults,
errfunc == 0 ? 0 : errfunc - 1);
} catch (const lutok::api_error& e) {
throw lutok::error("Failed to load Lua file '" + file + "': " +
e.what());
}
const int actual_results = s.get_top() - height;
assert(nresults == -1 || actual_results == nresults);
assert(actual_results >= 0);
return static_cast< unsigned int >(actual_results);
}
/// Processes a Lua script.
///
/// This is a replacement for luaL_dostring but with proper error reporting
/// and stack control.
///
/// \param s The Lua state.
/// \param str The string to process.
/// \param nargs The number of arguments on the stack to pass to the chunk.
/// \param nresults The number of results to expect; -1 for any.
/// \param errfunc If not 0, index of a function in the stack to act as an
/// error handler.
///
/// \return The number of results left on the stack.
///
/// \throw error If there is a problem processing the string.
unsigned int
lutok::do_string(state& s, const std::string& str, const int nargs,
const int nresults, const int errfunc)
{
assert(nresults >= -1);
const int height = s.get_top() - nargs;
try {
s.load_string(str);
if (nargs > 0)
s.insert(-nargs - 1);
s.pcall(nargs, nresults == -1 ? LUA_MULTRET : nresults,
errfunc == 0 ? 0 : errfunc - 1);
} catch (const lutok::api_error& e) {
throw lutok::error("Failed to process Lua string '" + str + "': " +
e.what());
}
const int actual_results = s.get_top() - height;
assert(nresults == -1 || actual_results == nresults);
assert(actual_results >= 0);
return static_cast< unsigned int >(actual_results);
}
/// Convenience function to evaluate a Lua expression.
///
/// \param s The Lua state.
/// \param expression The textual expression to evaluate.
/// \param nresults The number of results to leave on the stack. Must be
/// positive.
///
/// \throw api_error If there is a problem evaluating the expression.
void
lutok::eval(state& s, const std::string& expression, const int nresults)
{
assert(nresults > 0);
do_string(s, "return " + expression, 0, nresults, 0);
}

View file

@ -0,0 +1,55 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file operations.hpp
/// Extra generic functions to interact with Lua.
#if !defined(LUTOK_OPERATIONS_HPP)
#define LUTOK_OPERATIONS_HPP
#include <map>
#include <string>
#include <vector>
#include <lutok/state.hpp>
namespace lutok {
void create_module(state&, const std::string&,
const std::map< std::string, cxx_function >&);
unsigned int do_file(state&, const std::string&, const int, const int,
const int);
unsigned int do_string(state&, const std::string&, const int, const int,
const int);
void eval(state&, const std::string&, const int);
} // namespace lutok
#endif // !defined(LUTOK_OPERATIONS_HPP)

View file

@ -0,0 +1,372 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "operations.hpp"
#include <fstream>
#include <atf-c++.hpp>
#include "exceptions.hpp"
#include "state.ipp"
#include "test_utils.hpp"
namespace {
/// Addition function for injection into Lua.
///
/// \pre stack(-2) The first summand.
/// \pre stack(-1) The second summand.
/// \post stack(-1) The result of the sum.
///
/// \param state The Lua state.
///
/// \return The number of results (1).
static int
hook_add(lutok::state& state)
{
state.push_integer(state.to_integer(-1) + state.to_integer(-2));
return 1;
}
/// Multiplication function for injection into Lua.
///
/// \pre stack(-2) The first factor.
/// \pre stack(-1) The second factor.
/// \post stack(-1) The product.
///
/// \param state The Lua state.
///
/// \return The number of results (1).
static int
hook_multiply(lutok::state& state)
{
state.push_integer(state.to_integer(-1) * state.to_integer(-2));
return 1;
}
} // anonymous namespace
ATF_TEST_CASE_WITHOUT_HEAD(create_module__empty);
ATF_TEST_CASE_BODY(create_module__empty)
{
lutok::state state;
std::map< std::string, lutok::cxx_function > members;
lutok::create_module(state, "my_math", members);
state.open_base();
lutok::do_string(state, "return next(my_math) == nil", 0, 1, 0);
ATF_REQUIRE(state.to_boolean(-1));
state.pop(1);
}
ATF_TEST_CASE_WITHOUT_HEAD(create_module__one);
ATF_TEST_CASE_BODY(create_module__one)
{
lutok::state state;
std::map< std::string, lutok::cxx_function > members;
members["add"] = hook_add;
lutok::create_module(state, "my_math", members);
lutok::do_string(state, "return my_math.add(10, 20)", 0, 1, 0);
ATF_REQUIRE_EQ(30, state.to_integer(-1));
state.pop(1);
}
ATF_TEST_CASE_WITHOUT_HEAD(create_module__many);
ATF_TEST_CASE_BODY(create_module__many)
{
lutok::state state;
std::map< std::string, lutok::cxx_function > members;
members["add"] = hook_add;
members["multiply"] = hook_multiply;
members["add2"] = hook_add;
lutok::create_module(state, "my_math", members);
lutok::do_string(state, "return my_math.add(10, 20)", 0, 1, 0);
ATF_REQUIRE_EQ(30, state.to_integer(-1));
lutok::do_string(state, "return my_math.multiply(10, 20)", 0, 1, 0);
ATF_REQUIRE_EQ(200, state.to_integer(-1));
lutok::do_string(state, "return my_math.add2(20, 30)", 0, 1, 0);
ATF_REQUIRE_EQ(50, state.to_integer(-1));
state.pop(3);
}
ATF_TEST_CASE_WITHOUT_HEAD(do_file__some_args);
ATF_TEST_CASE_BODY(do_file__some_args)
{
std::ofstream output("test.lua");
output << "local a1, a2 = ...\nreturn a1 * 2, a2 * 2\n";
output.close();
lutok::state state;
state.push_integer(456);
state.push_integer(3);
state.push_integer(5);
state.push_integer(123);
ATF_REQUIRE_EQ(2, lutok::do_file(state, "test.lua", 3, -1, 0));
ATF_REQUIRE_EQ(3, state.get_top());
ATF_REQUIRE_EQ(456, state.to_integer(-3));
ATF_REQUIRE_EQ(6, state.to_integer(-2));
ATF_REQUIRE_EQ(10, state.to_integer(-1));
state.pop(3);
}
ATF_TEST_CASE_WITHOUT_HEAD(do_file__any_results);
ATF_TEST_CASE_BODY(do_file__any_results)
{
std::ofstream output("test.lua");
output << "return 10, 20, 30\n";
output.close();
lutok::state state;
ATF_REQUIRE_EQ(3, lutok::do_file(state, "test.lua", 0, -1, 0));
ATF_REQUIRE_EQ(3, state.get_top());
ATF_REQUIRE_EQ(10, state.to_integer(-3));
ATF_REQUIRE_EQ(20, state.to_integer(-2));
ATF_REQUIRE_EQ(30, state.to_integer(-1));
state.pop(3);
}
ATF_TEST_CASE_WITHOUT_HEAD(do_file__no_results);
ATF_TEST_CASE_BODY(do_file__no_results)
{
std::ofstream output("test.lua");
output << "return 10, 20, 30\n";
output.close();
lutok::state state;
ATF_REQUIRE_EQ(0, lutok::do_file(state, "test.lua", 0, 0, 0));
ATF_REQUIRE_EQ(0, state.get_top());
}
ATF_TEST_CASE_WITHOUT_HEAD(do_file__many_results);
ATF_TEST_CASE_BODY(do_file__many_results)
{
std::ofstream output("test.lua");
output << "return 10, 20, 30\n";
output.close();
lutok::state state;
ATF_REQUIRE_EQ(2, lutok::do_file(state, "test.lua", 0, 2, 0));
ATF_REQUIRE_EQ(2, state.get_top());
ATF_REQUIRE_EQ(10, state.to_integer(-2));
ATF_REQUIRE_EQ(20, state.to_integer(-1));
state.pop(2);
}
ATF_TEST_CASE_WITHOUT_HEAD(do_file__not_found);
ATF_TEST_CASE_BODY(do_file__not_found)
{
lutok::state state;
stack_balance_checker checker(state);
ATF_REQUIRE_THROW_RE(lutok::file_not_found_error, "missing.lua",
lutok::do_file(state, "missing.lua", 0, 0, 0));
}
ATF_TEST_CASE_WITHOUT_HEAD(do_file__error);
ATF_TEST_CASE_BODY(do_file__error)
{
std::ofstream output("test.lua");
output << "a b c\n";
output.close();
lutok::state state;
stack_balance_checker checker(state);
ATF_REQUIRE_THROW_RE(lutok::error, "Failed to load Lua file 'test.lua'",
lutok::do_file(state, "test.lua", 0, 0, 0));
}
ATF_TEST_CASE_WITHOUT_HEAD(do_file__error_with_errfunc);
ATF_TEST_CASE_BODY(do_file__error_with_errfunc)
{
std::ofstream output("test.lua");
output << "unknown_function()\n";
output.close();
lutok::state state;
lutok::eval(state, "function(message) return 'This is an error!' end", 1);
{
stack_balance_checker checker(state);
ATF_REQUIRE_THROW_RE(lutok::error, "This is an error!",
lutok::do_file(state, "test.lua", 0, 0, -2));
}
state.pop(1);
}
ATF_TEST_CASE_WITHOUT_HEAD(do_string__some_args);
ATF_TEST_CASE_BODY(do_string__some_args)
{
lutok::state state;
state.push_integer(456);
state.push_integer(3);
state.push_integer(5);
state.push_integer(123);
ATF_REQUIRE_EQ(2, lutok::do_string(
state, "local a1, a2 = ...\nreturn a1 * 2, a2 * 2\n", 3, -1, 0));
ATF_REQUIRE_EQ(3, state.get_top());
ATF_REQUIRE_EQ(456, state.to_integer(-3));
ATF_REQUIRE_EQ(6, state.to_integer(-2));
ATF_REQUIRE_EQ(10, state.to_integer(-1));
state.pop(3);
}
ATF_TEST_CASE_WITHOUT_HEAD(do_string__any_results);
ATF_TEST_CASE_BODY(do_string__any_results)
{
lutok::state state;
ATF_REQUIRE_EQ(3, lutok::do_string(state, "return 10, 20, 30", 0, -1, 0));
ATF_REQUIRE_EQ(3, state.get_top());
ATF_REQUIRE_EQ(10, state.to_integer(-3));
ATF_REQUIRE_EQ(20, state.to_integer(-2));
ATF_REQUIRE_EQ(30, state.to_integer(-1));
state.pop(3);
}
ATF_TEST_CASE_WITHOUT_HEAD(do_string__no_results);
ATF_TEST_CASE_BODY(do_string__no_results)
{
lutok::state state;
ATF_REQUIRE_EQ(0, lutok::do_string(state, "return 10, 20, 30", 0, 0, 0));
ATF_REQUIRE_EQ(0, state.get_top());
}
ATF_TEST_CASE_WITHOUT_HEAD(do_string__many_results);
ATF_TEST_CASE_BODY(do_string__many_results)
{
lutok::state state;
ATF_REQUIRE_EQ(2, lutok::do_string(state, "return 10, 20, 30", 0, 2, 0));
ATF_REQUIRE_EQ(2, state.get_top());
ATF_REQUIRE_EQ(10, state.to_integer(-2));
ATF_REQUIRE_EQ(20, state.to_integer(-1));
state.pop(2);
}
ATF_TEST_CASE_WITHOUT_HEAD(do_string__error);
ATF_TEST_CASE_BODY(do_string__error)
{
lutok::state state;
stack_balance_checker checker(state);
ATF_REQUIRE_THROW_RE(lutok::error, "Failed to process Lua string 'a b c'",
lutok::do_string(state, "a b c", 0, 0, 0));
}
ATF_TEST_CASE_WITHOUT_HEAD(do_string__error_with_errfunc);
ATF_TEST_CASE_BODY(do_string__error_with_errfunc)
{
lutok::state state;
lutok::eval(state, "function(message) return 'This is an error!' end", 1);
{
stack_balance_checker checker(state);
ATF_REQUIRE_THROW_RE(lutok::error, "This is an error!",
lutok::do_string(state, "unknown_function()",
0, 0, -2));
}
state.pop(1);
}
ATF_TEST_CASE_WITHOUT_HEAD(eval__one_result);
ATF_TEST_CASE_BODY(eval__one_result)
{
lutok::state state;
stack_balance_checker checker(state);
lutok::eval(state, "3 + 10", 1);
ATF_REQUIRE_EQ(13, state.to_integer(-1));
state.pop(1);
}
ATF_TEST_CASE_WITHOUT_HEAD(eval__many_results);
ATF_TEST_CASE_BODY(eval__many_results)
{
lutok::state state;
stack_balance_checker checker(state);
lutok::eval(state, "5, 8, 10", 3);
ATF_REQUIRE_EQ(5, state.to_integer(-3));
ATF_REQUIRE_EQ(8, state.to_integer(-2));
ATF_REQUIRE_EQ(10, state.to_integer(-1));
state.pop(3);
}
ATF_TEST_CASE_WITHOUT_HEAD(eval__error);
ATF_TEST_CASE_BODY(eval__error)
{
lutok::state state;
stack_balance_checker checker(state);
ATF_REQUIRE_THROW(lutok::error,
lutok::eval(state, "non_existent.method()", 1));
}
ATF_INIT_TEST_CASES(tcs)
{
ATF_ADD_TEST_CASE(tcs, create_module__empty);
ATF_ADD_TEST_CASE(tcs, create_module__one);
ATF_ADD_TEST_CASE(tcs, create_module__many);
ATF_ADD_TEST_CASE(tcs, do_file__some_args);
ATF_ADD_TEST_CASE(tcs, do_file__any_results);
ATF_ADD_TEST_CASE(tcs, do_file__no_results);
ATF_ADD_TEST_CASE(tcs, do_file__many_results);
ATF_ADD_TEST_CASE(tcs, do_file__not_found);
ATF_ADD_TEST_CASE(tcs, do_file__error);
ATF_ADD_TEST_CASE(tcs, do_file__error_with_errfunc);
ATF_ADD_TEST_CASE(tcs, do_string__some_args);
ATF_ADD_TEST_CASE(tcs, do_string__any_results);
ATF_ADD_TEST_CASE(tcs, do_string__no_results);
ATF_ADD_TEST_CASE(tcs, do_string__many_results);
ATF_ADD_TEST_CASE(tcs, do_string__error);
ATF_ADD_TEST_CASE(tcs, do_string__error_with_errfunc);
ATF_ADD_TEST_CASE(tcs, eval__one_result);
ATF_ADD_TEST_CASE(tcs, eval__many_results);
ATF_ADD_TEST_CASE(tcs, eval__error);
}

View file

@ -0,0 +1,91 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cassert>
#include <cstring>
#include "stack_cleaner.hpp"
#include "state.ipp"
/// Internal implementation for lutok::stack_cleaner.
struct lutok::stack_cleaner::impl {
/// Reference to the Lua state this stack_cleaner refers to.
state& state_ref;
/// The depth of the Lua stack to be restored.
unsigned int original_depth;
/// Constructor.
///
/// \param state_ref_ Reference to the Lua state.
/// \param original_depth_ The depth of the Lua stack.
impl(state& state_ref_, const unsigned int original_depth_) :
state_ref(state_ref_),
original_depth(original_depth_)
{
}
};
/// Creates a new stack cleaner.
///
/// This gathers the current height of the stack so that extra elements can be
/// popped during destruction.
///
/// \param state_ The Lua state.
lutok::stack_cleaner::stack_cleaner(state& state_) :
_pimpl(new impl(state_, state_.get_top()))
{
}
/// Pops any values from the stack not known at construction time.
///
/// \pre The current height of the stack must be equal or greater to the height
/// of the stack when this object was instantiated.
lutok::stack_cleaner::~stack_cleaner(void)
{
const unsigned int current_depth = _pimpl->state_ref.get_top();
assert(current_depth >= _pimpl->original_depth);
const unsigned int diff = current_depth - _pimpl->original_depth;
if (diff > 0)
_pimpl->state_ref.pop(diff);
}
/// Forgets about any elements currently in the stack.
///
/// This allows a function to return values on the stack because all the
/// elements that are currently in the stack will not be touched during
/// destruction when the function is called.
void
lutok::stack_cleaner::forget(void)
{
_pimpl->original_depth = _pimpl->state_ref.get_top();
}

View file

@ -0,0 +1,93 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file stack_cleaner.hpp
/// Provides the stack_cleaner class.
#if !defined(LUTOK_STACK_CLEANER_HPP)
#define LUTOK_STACK_CLEANER_HPP
#include <memory>
#include <lutok/state.hpp>
namespace lutok {
/// A RAII model for values on the Lua stack.
///
/// At creation time, the object records the current depth of the Lua stack and,
/// during destruction, restores the recorded depth by popping as many stack
/// entries as required. As a corollary, the stack can only grow during the
/// lifetime of a stack_cleaner object (or shrink, but cannot become shorter
/// than the depth recorded at creation time).
///
/// Use this class as follows:
///
/// state s;
/// {
/// stack_cleaner cleaner1(s);
/// s.push_integer(3);
/// s.push_integer(5);
/// ... do stuff here ...
/// for (...) {
/// stack_cleaner cleaner2(s);
/// s.load_string("...");
/// s.pcall(0, 1, 0);
/// ... do stuff here ...
/// }
/// // cleaner2 destroyed; the result of pcall is gone.
/// }
/// // cleaner1 destroyed; the integers 3 and 5 are gone.
///
/// You must give a name to the instantiated objects even if they cannot be
/// accessed later. Otherwise, the instance will be destroyed right away and
/// will not have the desired effect.
class stack_cleaner {
struct impl;
/// Pointer to the shared internal implementation.
std::auto_ptr< impl > _pimpl;
/// Disallow copies.
stack_cleaner(const stack_cleaner&);
/// Disallow assignment.
stack_cleaner& operator=(const stack_cleaner&);
public:
stack_cleaner(state&);
~stack_cleaner(void);
void forget(void);
};
} // namespace lutok
#endif // !defined(LUTOK_STACK_CLEANER_HPP)

View file

@ -0,0 +1,108 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "stack_cleaner.hpp"
#include <atf-c++.hpp>
ATF_TEST_CASE_WITHOUT_HEAD(empty);
ATF_TEST_CASE_BODY(empty)
{
lutok::state state;
{
lutok::stack_cleaner cleaner(state);
ATF_REQUIRE_EQ(0, state.get_top());
}
ATF_REQUIRE_EQ(0, state.get_top());
}
ATF_TEST_CASE_WITHOUT_HEAD(some);
ATF_TEST_CASE_BODY(some)
{
lutok::state state;
{
lutok::stack_cleaner cleaner(state);
state.push_integer(15);
ATF_REQUIRE_EQ(1, state.get_top());
state.push_integer(30);
ATF_REQUIRE_EQ(2, state.get_top());
}
ATF_REQUIRE_EQ(0, state.get_top());
}
ATF_TEST_CASE_WITHOUT_HEAD(nested);
ATF_TEST_CASE_BODY(nested)
{
lutok::state state;
{
lutok::stack_cleaner cleaner1(state);
state.push_integer(10);
ATF_REQUIRE_EQ(1, state.get_top());
ATF_REQUIRE_EQ(10, state.to_integer(-1));
{
lutok::stack_cleaner cleaner2(state);
state.push_integer(20);
ATF_REQUIRE_EQ(2, state.get_top());
ATF_REQUIRE_EQ(20, state.to_integer(-1));
ATF_REQUIRE_EQ(10, state.to_integer(-2));
}
ATF_REQUIRE_EQ(1, state.get_top());
ATF_REQUIRE_EQ(10, state.to_integer(-1));
}
ATF_REQUIRE_EQ(0, state.get_top());
}
ATF_TEST_CASE_WITHOUT_HEAD(forget);
ATF_TEST_CASE_BODY(forget)
{
lutok::state state;
{
lutok::stack_cleaner cleaner(state);
state.push_integer(15);
state.push_integer(30);
cleaner.forget();
state.push_integer(60);
ATF_REQUIRE_EQ(3, state.get_top());
}
ATF_REQUIRE_EQ(2, state.get_top());
ATF_REQUIRE_EQ(30, state.to_integer(-1));
state.pop(2);
}
ATF_INIT_TEST_CASES(tcs)
{
ATF_ADD_TEST_CASE(tcs, empty);
ATF_ADD_TEST_CASE(tcs, some);
ATF_ADD_TEST_CASE(tcs, nested);
ATF_ADD_TEST_CASE(tcs, forget);
}

904
contrib/lutok/state.cpp Normal file
View file

@ -0,0 +1,904 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
extern "C" {
#include <unistd.h>
}
#include <cassert>
#include <cstring>
#include "c_gate.hpp"
#include "exceptions.hpp"
#include "state.ipp"
namespace {
/// Wrapper around lua_getglobal to run in a protected environment.
///
/// \pre stack(-1) is the name of the global to get.
/// \post stack(-1) is the value of the global.
///
/// \param state The Lua C API state.
///
/// \return The number of return values pushed onto the stack.
static int
protected_getglobal(lua_State* state)
{
lua_getglobal(state, lua_tostring(state, -1));
return 1;
}
/// Wrapper around lua_gettable to run in a protected environment.
///
/// \pre stack(-2) is the table to get the element from.
/// \pre stack(-1) is the table index.
/// \post stack(-1) is the value of stack(-2)[stack(-1)].
///
/// \param state The Lua C API state.
///
/// \return The number of return values pushed onto the stack.
static int
protected_gettable(lua_State* state)
{
lua_gettable(state, -2);
return 1;
}
/// Wrapper around lua_next to run in a protected environment.
///
/// \pre stack(-2) is the table to get the next element from.
/// \pre stack(-1) is the last processed key.
/// \post stack(-1) is the value of next(stack(-2), stack(-1)).
///
/// \param state The Lua C API state.
///
/// \return The number of return values pushed onto the stack.
static int
protected_next(lua_State* state)
{
const int more = lua_next(state, -2) != 0;
lua_pushboolean(state, more);
return more ? 3 : 1;
}
/// Wrapper around lua_setglobal to run in a protected environment.
///
/// \pre stack(-2) is the name of the global to set.
/// \pre stack(-1) is the value to set the global to.
///
/// \param state The Lua C API state.
///
/// \return The number of return values pushed onto the stack.
static int
protected_setglobal(lua_State* state)
{
lua_setglobal(state, lua_tostring(state, -2));
return 0;
}
/// Wrapper around lua_settable to run in a protected environment.
///
/// \pre stack(-3) is the table to set the element into.
/// \pre stack(-2) is the table index.
/// \pre stack(-1) is the value to set.
///
/// \param state The Lua C API state.
///
/// \return The number of return values pushed onto the stack.
static int
protected_settable(lua_State* state)
{
lua_settable(state, -3);
return 0;
}
/// Calls a C++ Lua function from a C calling environment.
///
/// Any errors reported by the C++ function are caught and reported to the
/// caller as Lua errors.
///
/// \param function The C++ function to call.
/// \param raw_state The raw Lua state.
///
/// \return The number of return values pushed onto the Lua stack by the
/// function.
static int
call_cxx_function_from_c(lutok::cxx_function function,
lua_State* raw_state) throw()
{
char error_buf[1024];
try {
lutok::state state = lutok::state_c_gate::connect(raw_state);
return function(state);
} catch (const std::exception& e) {
std::strncpy(error_buf, e.what(), sizeof(error_buf));
} catch (...) {
std::strncpy(error_buf, "Unhandled exception in Lua C++ hook",
sizeof(error_buf));
}
error_buf[sizeof(error_buf) - 1] = '\0';
// We raise the Lua error from outside the try/catch context and we use
// a stack-based buffer to hold the message to ensure that we do not leak
// any C++ objects (and, as a likely result, memory) when Lua performs its
// longjmp.
return luaL_error(raw_state, "%s", error_buf);
}
/// Lua glue to call a C++ closure.
///
/// This Lua binding is actually a closure that we have constructed from the
/// state.push_cxx_closure() method. The closure contains the same upvalues
/// provided by the user plus an extra upvalue that contains the address of the
/// C++ function we have to call. All we do here is safely delegate the
/// execution to the wrapped C++ closure.
///
/// \param raw_state The Lua C API state.
///
/// \return The number of return values of the called closure.
static int
cxx_closure_trampoline(lua_State* raw_state)
{
lutok::state state = lutok::state_c_gate::connect(raw_state);
int nupvalues;
{
lua_Debug debug;
lua_getstack(raw_state, 0, &debug);
lua_getinfo(raw_state, "u", &debug);
nupvalues = debug.nups;
}
lutok::cxx_function* function = state.to_userdata< lutok::cxx_function >(
state.upvalue_index(nupvalues));
return call_cxx_function_from_c(*function, raw_state);
}
/// Lua glue to call a C++ function.
///
/// This Lua binding is actually a closure that we have constructed from the
/// state.push_cxx_function() method. The closure has a single upvalue that
/// contains the address of the C++ function we have to call. All we do here is
/// safely delegate the execution to the wrapped C++ function.
///
/// \param raw_state The Lua C API state.
///
/// \return The number of return values of the called function.
static int
cxx_function_trampoline(lua_State* raw_state)
{
lutok::state state = lutok::state_c_gate::connect(raw_state);
lutok::cxx_function* function = state.to_userdata< lutok::cxx_function >(
state.upvalue_index(1));
return call_cxx_function_from_c(*function, raw_state);
}
} // anonymous namespace
const int lutok::registry_index = LUA_REGISTRYINDEX;
/// Internal implementation for lutok::state.
struct lutok::state::impl {
/// The Lua internal state.
lua_State* lua_state;
/// Whether we own the state or not (to decide if we close it).
bool owned;
/// Constructor.
///
/// \param lua_ The Lua internal state.
/// \param owned_ Whether we own the state or not.
impl(lua_State* lua_, bool owned_) :
lua_state(lua_),
owned(owned_)
{
}
};
/// Initializes the Lua state.
///
/// You must share the same state object alongside the lifetime of your Lua
/// session. As soon as the object is destroyed, the session is terminated.
lutok::state::state(void)
{
lua_State* lua = luaL_newstate();
if (lua == NULL)
throw lutok::error("lua open failed");
_pimpl.reset(new impl(lua, true));
}
/// Initializes the Lua state from an existing raw state.
///
/// Instances constructed using this method do NOT own the raw state. This
/// means that, on exit, the state will not be destroyed.
///
/// \param raw_state_ The raw Lua state to wrap.
lutok::state::state(void* raw_state_) :
_pimpl(new impl(reinterpret_cast< lua_State* >(raw_state_), false))
{
}
/// Destructor for the Lua state.
///
/// Closes the session unless it has already been closed by calling the
/// close() method. It is recommended to explicitly close the session in the
/// code.
lutok::state::~state(void)
{
if (_pimpl->owned && _pimpl->lua_state != NULL)
close();
}
/// Terminates this Lua session.
///
/// It is recommended to call this instead of relying on the destructor to do
/// the cleanup, but it is not a requirement to use close().
///
/// \pre close() has not yet been called.
/// \pre The Lua stack is empty. This is not truly necessary but ensures that
/// our code is consistent and clears the stack explicitly.
void
lutok::state::close(void)
{
assert(_pimpl->lua_state != NULL);
assert(lua_gettop(_pimpl->lua_state) == 0);
lua_close(_pimpl->lua_state);
_pimpl->lua_state = NULL;
}
/// Wrapper around lua_getglobal.
///
/// \param name The second parameter to lua_getglobal.
///
/// \throw api_error If lua_getglobal fails.
///
/// \warning Terminates execution if there is not enough memory to manipulate
/// the Lua stack.
void
lutok::state::get_global(const std::string& name)
{
lua_pushcfunction(_pimpl->lua_state, protected_getglobal);
lua_pushstring(_pimpl->lua_state, name.c_str());
if (lua_pcall(_pimpl->lua_state, 1, 1, 0) != 0)
throw lutok::api_error::from_stack(*this, "lua_getglobal");
}
/// Pushes a reference to the global table onto the stack.
///
/// This is a wrapper around the incompatible differences between Lua 5.1 and
/// 5.2 to access to the globals table.
///
/// \post state(-1) Contains the reference to the globals table.
void
lutok::state::get_global_table(void)
{
#if LUA_VERSION_NUM >= 502
lua_pushvalue(_pimpl->lua_state, registry_index);
lua_pushinteger(_pimpl->lua_state, LUA_RIDX_GLOBALS);
lua_gettable(_pimpl->lua_state, -2);
lua_remove(_pimpl->lua_state, -2);
#else
lua_pushvalue(_pimpl->lua_state, LUA_GLOBALSINDEX);
#endif
}
/// Wrapper around luaL_getmetafield.
///
/// \param index The second parameter to luaL_getmetafield.
/// \param name The third parameter to luaL_getmetafield.
///
/// \return The return value of luaL_getmetafield.
///
/// \warning Terminates execution if there is not enough memory to manipulate
/// the Lua stack.
bool
lutok::state::get_metafield(const int index, const std::string& name)
{
return luaL_getmetafield(_pimpl->lua_state, index, name.c_str()) != 0;
}
/// Wrapper around lua_getmetatable.
///
/// \param index The second parameter to lua_getmetatable.
///
/// \return The return value of lua_getmetatable.
bool
lutok::state::get_metatable(const int index)
{
return lua_getmetatable(_pimpl->lua_state, index) != 0;
}
/// Wrapper around lua_gettable.
///
/// \param index The second parameter to lua_gettable.
///
/// \throw api_error If lua_gettable fails.
///
/// \warning Terminates execution if there is not enough memory to manipulate
/// the Lua stack.
void
lutok::state::get_table(const int index)
{
assert(lua_gettop(_pimpl->lua_state) >= 2);
lua_pushcfunction(_pimpl->lua_state, protected_gettable);
lua_pushvalue(_pimpl->lua_state, index < 0 ? index - 1 : index);
lua_pushvalue(_pimpl->lua_state, -3);
if (lua_pcall(_pimpl->lua_state, 2, 1, 0) != 0)
throw lutok::api_error::from_stack(*this, "lua_gettable");
lua_remove(_pimpl->lua_state, -2);
}
/// Wrapper around lua_gettop.
///
/// \return The return value of lua_gettop.
int
lutok::state::get_top(void)
{
return lua_gettop(_pimpl->lua_state);
}
/// Wrapper around lua_insert.
///
/// \param index The second parameter to lua_insert.
void
lutok::state::insert(const int index)
{
lua_insert(_pimpl->lua_state, index);
}
/// Wrapper around lua_isboolean.
///
/// \param index The second parameter to lua_isboolean.
///
/// \return The return value of lua_isboolean.
bool
lutok::state::is_boolean(const int index)
{
return lua_isboolean(_pimpl->lua_state, index);
}
/// Wrapper around lua_isfunction.
///
/// \param index The second parameter to lua_isfunction.
///
/// \return The return value of lua_isfunction.
bool
lutok::state::is_function(const int index)
{
return lua_isfunction(_pimpl->lua_state, index);
}
/// Wrapper around lua_isnil.
///
/// \param index The second parameter to lua_isnil.
///
/// \return The return value of lua_isnil.
bool
lutok::state::is_nil(const int index)
{
return lua_isnil(_pimpl->lua_state, index);
}
/// Wrapper around lua_isnumber.
///
/// \param index The second parameter to lua_isnumber.
///
/// \return The return value of lua_isnumber.
bool
lutok::state::is_number(const int index)
{
return lua_isnumber(_pimpl->lua_state, index);
}
/// Wrapper around lua_isstring.
///
/// \param index The second parameter to lua_isstring.
///
/// \return The return value of lua_isstring.
bool
lutok::state::is_string(const int index)
{
return lua_isstring(_pimpl->lua_state, index);
}
/// Wrapper around lua_istable.
///
/// \param index The second parameter to lua_istable.
///
/// \return The return value of lua_istable.
bool
lutok::state::is_table(const int index)
{
return lua_istable(_pimpl->lua_state, index);
}
/// Wrapper around lua_isuserdata.
///
/// \param index The second parameter to lua_isuserdata.
///
/// \return The return value of lua_isuserdata.
bool
lutok::state::is_userdata(const int index)
{
return lua_isuserdata(_pimpl->lua_state, index);
}
/// Wrapper around luaL_loadfile.
///
/// \param file The second parameter to luaL_loadfile.
///
/// \throw api_error If luaL_loadfile returns an error.
/// \throw file_not_found_error If the file cannot be accessed.
///
/// \warning Terminates execution if there is not enough memory.
void
lutok::state::load_file(const std::string& file)
{
if (::access(file.c_str(), R_OK) == -1)
throw lutok::file_not_found_error(file);
if (luaL_loadfile(_pimpl->lua_state, file.c_str()) != 0)
throw lutok::api_error::from_stack(*this, "luaL_loadfile");
}
/// Wrapper around luaL_loadstring.
///
/// \param str The second parameter to luaL_loadstring.
///
/// \throw api_error If luaL_loadstring returns an error.
///
/// \warning Terminates execution if there is not enough memory.
void
lutok::state::load_string(const std::string& str)
{
if (luaL_loadstring(_pimpl->lua_state, str.c_str()) != 0)
throw lutok::api_error::from_stack(*this, "luaL_loadstring");
}
/// Wrapper around lua_newtable.
///
/// \warning Terminates execution if there is not enough memory.
void
lutok::state::new_table(void)
{
lua_newtable(_pimpl->lua_state);
}
/// Wrapper around lua_newuserdata.
///
/// This is internal. The public type-safe interface of this method should be
/// used instead.
///
/// \param size The second parameter to lua_newuserdata.
///
/// \return The return value of lua_newuserdata.
///
/// \warning Terminates execution if there is not enough memory.
void*
lutok::state::new_userdata_voidp(const size_t size)
{
return lua_newuserdata(_pimpl->lua_state, size);
}
/// Wrapper around lua_next.
///
/// \param index The second parameter to lua_next.
///
/// \return True if there are more elements to process; false otherwise.
///
/// \warning Terminates execution if there is not enough memory.
bool
lutok::state::next(const int index)
{
assert(lua_istable(_pimpl->lua_state, index));
assert(lua_gettop(_pimpl->lua_state) >= 1);
lua_pushcfunction(_pimpl->lua_state, protected_next);
lua_pushvalue(_pimpl->lua_state, index < 0 ? index - 1 : index);
lua_pushvalue(_pimpl->lua_state, -3);
if (lua_pcall(_pimpl->lua_state, 2, LUA_MULTRET, 0) != 0)
throw lutok::api_error::from_stack(*this, "lua_next");
const bool more = lua_toboolean(_pimpl->lua_state, -1);
lua_pop(_pimpl->lua_state, 1);
if (more)
lua_remove(_pimpl->lua_state, -3);
else
lua_pop(_pimpl->lua_state, 1);
return more;
}
/// Wrapper around luaL_openlibs.
///
/// \throw api_error If luaL_openlibs fails.
///
/// \warning Terminates execution if there is not enough memory.
void
lutok::state::open_all(void)
{
luaL_openlibs(_pimpl->lua_state);
}
/// Wrapper around luaopen_base.
///
/// \throw api_error If luaopen_base fails.
///
/// \warning Terminates execution if there is not enough memory.
void
lutok::state::open_base(void)
{
lua_pushcfunction(_pimpl->lua_state, luaopen_base);
if (lua_pcall(_pimpl->lua_state, 0, 0, 0) != 0)
throw lutok::api_error::from_stack(*this, "luaopen_base");
}
/// Wrapper around luaopen_string.
///
/// \throw api_error If luaopen_string fails.
///
/// \warning Terminates execution if there is not enough memory.
void
lutok::state::open_string(void)
{
#if LUA_VERSION_NUM >= 502
luaL_requiref(_pimpl->lua_state, LUA_STRLIBNAME, luaopen_string, 1);
lua_pop(_pimpl->lua_state, 1);
#else
lua_pushcfunction(_pimpl->lua_state, luaopen_string);
if (lua_pcall(_pimpl->lua_state, 0, 0, 0) != 0)
throw lutok::api_error::from_stack(*this, "luaopen_string");
#endif
}
/// Wrapper around luaopen_table.
///
/// \throw api_error If luaopen_table fails.
///
/// \warning Terminates execution if there is not enough memory.
void
lutok::state::open_table(void)
{
#if LUA_VERSION_NUM >= 502
luaL_requiref(_pimpl->lua_state, LUA_TABLIBNAME, luaopen_table, 1);
lua_pop(_pimpl->lua_state, 1);
#else
lua_pushcfunction(_pimpl->lua_state, luaopen_table);
if (lua_pcall(_pimpl->lua_state, 0, 0, 0) != 0)
throw lutok::api_error::from_stack(*this, "luaopen_table");
#endif
}
/// Wrapper around lua_pcall.
///
/// \param nargs The second parameter to lua_pcall.
/// \param nresults The third parameter to lua_pcall.
/// \param errfunc The fourth parameter to lua_pcall.
///
/// \throw api_error If lua_pcall returns an error.
void
lutok::state::pcall(const int nargs, const int nresults, const int errfunc)
{
if (lua_pcall(_pimpl->lua_state, nargs, nresults, errfunc) != 0)
throw lutok::api_error::from_stack(*this, "lua_pcall");
}
/// Wrapper around lua_pop.
///
/// \param count The second parameter to lua_pop.
void
lutok::state::pop(const int count)
{
assert(count <= lua_gettop(_pimpl->lua_state));
lua_pop(_pimpl->lua_state, count);
assert(lua_gettop(_pimpl->lua_state) >= 0);
}
/// Wrapper around lua_pushboolean.
///
/// \param value The second parameter to lua_pushboolean.
void
lutok::state::push_boolean(const bool value)
{
lua_pushboolean(_pimpl->lua_state, value ? 1 : 0);
}
/// Wrapper around lua_pushcclosure.
///
/// This is not a pure wrapper around lua_pushcclosure because this has to do
/// extra magic to allow passing C++ functions instead of plain C functions.
///
/// \param function The C++ function to be pushed as a closure.
/// \param nvalues The number of upvalues that the function receives.
void
lutok::state::push_cxx_closure(cxx_function function, const int nvalues)
{
cxx_function *data = static_cast< cxx_function* >(
lua_newuserdata(_pimpl->lua_state, sizeof(cxx_function)));
*data = function;
lua_pushcclosure(_pimpl->lua_state, cxx_closure_trampoline, nvalues + 1);
}
/// Wrapper around lua_pushcfunction.
///
/// This is not a pure wrapper around lua_pushcfunction because this has to do
/// extra magic to allow passing C++ functions instead of plain C functions.
///
/// \param function The C++ function to be pushed.
void
lutok::state::push_cxx_function(cxx_function function)
{
cxx_function *data = static_cast< cxx_function* >(
lua_newuserdata(_pimpl->lua_state, sizeof(cxx_function)));
*data = function;
lua_pushcclosure(_pimpl->lua_state, cxx_function_trampoline, 1);
}
/// Wrapper around lua_pushinteger.
///
/// \param value The second parameter to lua_pushinteger.
void
lutok::state::push_integer(const int value)
{
lua_pushinteger(_pimpl->lua_state, value);
}
/// Wrapper around lua_pushnil.
void
lutok::state::push_nil(void)
{
lua_pushnil(_pimpl->lua_state);
}
/// Wrapper around lua_pushstring.
///
/// \param str The second parameter to lua_pushstring.
///
/// \warning Terminates execution if there is not enough memory.
void
lutok::state::push_string(const std::string& str)
{
lua_pushstring(_pimpl->lua_state, str.c_str());
}
/// Wrapper around lua_pushvalue.
///
/// \param index The second parameter to lua_pushvalue.
void
lutok::state::push_value(const int index)
{
lua_pushvalue(_pimpl->lua_state, index);
}
/// Wrapper around lua_rawget.
///
/// \param index The second parameter to lua_rawget.
void
lutok::state::raw_get(const int index)
{
lua_rawget(_pimpl->lua_state, index);
}
/// Wrapper around lua_rawset.
///
/// \param index The second parameter to lua_rawset.
///
/// \warning Terminates execution if there is not enough memory to manipulate
/// the Lua stack.
void
lutok::state::raw_set(const int index)
{
lua_rawset(_pimpl->lua_state, index);
}
/// Wrapper around lua_setglobal.
///
/// \param name The second parameter to lua_setglobal.
///
/// \throw api_error If lua_setglobal fails.
///
/// \warning Terminates execution if there is not enough memory to manipulate
/// the Lua stack.
void
lutok::state::set_global(const std::string& name)
{
lua_pushcfunction(_pimpl->lua_state, protected_setglobal);
lua_pushstring(_pimpl->lua_state, name.c_str());
lua_pushvalue(_pimpl->lua_state, -3);
if (lua_pcall(_pimpl->lua_state, 2, 0, 0) != 0)
throw lutok::api_error::from_stack(*this, "lua_setglobal");
lua_pop(_pimpl->lua_state, 1);
}
/// Wrapper around lua_setmetatable.
///
/// \param index The second parameter to lua_setmetatable.
void
lutok::state::set_metatable(const int index)
{
lua_setmetatable(_pimpl->lua_state, index);
}
/// Wrapper around lua_settable.
///
/// \param index The second parameter to lua_settable.
///
/// \throw api_error If lua_settable fails.
///
/// \warning Terminates execution if there is not enough memory to manipulate
/// the Lua stack.
void
lutok::state::set_table(const int index)
{
lua_pushcfunction(_pimpl->lua_state, protected_settable);
lua_pushvalue(_pimpl->lua_state, index < 0 ? index - 1 : index);
lua_pushvalue(_pimpl->lua_state, -4);
lua_pushvalue(_pimpl->lua_state, -4);
if (lua_pcall(_pimpl->lua_state, 3, 0, 0) != 0)
throw lutok::api_error::from_stack(*this, "lua_settable");
lua_pop(_pimpl->lua_state, 2);
}
/// Wrapper around lua_toboolean.
///
/// \param index The second parameter to lua_toboolean.
///
/// \return The return value of lua_toboolean.
bool
lutok::state::to_boolean(const int index)
{
assert(is_boolean(index));
return lua_toboolean(_pimpl->lua_state, index);
}
/// Wrapper around lua_tointeger.
///
/// \param index The second parameter to lua_tointeger.
///
/// \return The return value of lua_tointeger.
long
lutok::state::to_integer(const int index)
{
assert(is_number(index));
return lua_tointeger(_pimpl->lua_state, index);
}
/// Wrapper around lua_touserdata.
///
/// This is internal. The public type-safe interface of this method should be
/// used instead.
///
/// \param index The second parameter to lua_touserdata.
///
/// \return The return value of lua_touserdata.
///
/// \warning Terminates execution if there is not enough memory.
void*
lutok::state::to_userdata_voidp(const int index)
{
return lua_touserdata(_pimpl->lua_state, index);
}
/// Wrapper around lua_tostring.
///
/// \param index The second parameter to lua_tostring.
///
/// \return The return value of lua_tostring.
///
/// \warning Terminates execution if there is not enough memory.
std::string
lutok::state::to_string(const int index)
{
assert(is_string(index));
const char *raw_string = lua_tostring(_pimpl->lua_state, index);
// Note that the creation of a string object below (explicit for clarity)
// implies that the raw string is duplicated and, henceforth, the string is
// safe even if the corresponding element is popped from the Lua stack.
return std::string(raw_string);
}
/// Wrapper around lua_upvalueindex.
///
/// \param index The first parameter to lua_upvalueindex.
///
/// \return The return value of lua_upvalueindex.
int
lutok::state::upvalue_index(const int index)
{
return lua_upvalueindex(index);
}
/// Gets the internal lua_State object.
///
/// \return The raw Lua state. This is returned as a void pointer to prevent
/// including the lua.hpp header file from our public interface. The only way
/// to call this method is by using the c_gate module, and c_gate takes care of
/// casting this object to the appropriate type.
void*
lutok::state::raw_state(void)
{
return _pimpl->lua_state;
}

145
contrib/lutok/state.hpp Normal file
View file

@ -0,0 +1,145 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file state.hpp
/// Provides the state wrapper class for the Lua C state.
#if !defined(LUTOK_STATE_HPP)
#define LUTOK_STATE_HPP
#include <string>
#if defined(_LIBCPP_VERSION) || __cplusplus >= 201103L
#include <memory>
#else
#include <tr1/memory>
#endif
namespace lutok {
class debug;
class state;
/// The type of a C++ function that can be bound into Lua.
///
/// Functions of this type are free to raise exceptions. These will not
/// propagate into the Lua C API. However, any such exceptions will be reported
/// as a Lua error and their type will be lost.
typedef int (*cxx_function)(state&);
/// Stack index constant pointing to the registry table.
extern const int registry_index;
/// A RAII model for the Lua state.
///
/// This class holds the state of the Lua interpreter during its existence and
/// provides wrappers around several Lua library functions that operate on such
/// state.
///
/// These wrapper functions differ from the C versions in that they use the
/// implicit state hold by the class, they use C++ types where appropriate and
/// they use exceptions to report errors.
///
/// The wrappers intend to be as lightweight as possible but, in some
/// situations, they are pretty complex because they need to do extra work to
/// capture the errors reported by the Lua C API. We prefer having fine-grained
/// error control rather than efficiency, so this is OK.
class state {
struct impl;
/// Pointer to the shared internal implementation.
#if defined(_LIBCPP_VERSION) || __cplusplus >= 201103L
std::shared_ptr< impl > _pimpl;
#else
std::tr1::shared_ptr< impl > _pimpl;
#endif
void* new_userdata_voidp(const size_t);
void* to_userdata_voidp(const int);
friend class state_c_gate;
explicit state(void*);
void* raw_state(void);
public:
state(void);
~state(void);
void close(void);
void get_global(const std::string&);
void get_global_table(void);
bool get_metafield(const int, const std::string&);
bool get_metatable(const int);
void get_table(const int);
int get_top(void);
void insert(const int);
bool is_boolean(const int);
bool is_function(const int);
bool is_nil(const int);
bool is_number(const int);
bool is_string(const int);
bool is_table(const int);
bool is_userdata(const int);
void load_file(const std::string&);
void load_string(const std::string&);
void new_table(void);
template< typename Type > Type* new_userdata(void);
bool next(const int);
void open_all(void);
void open_base(void);
void open_string(void);
void open_table(void);
void pcall(const int, const int, const int);
void pop(const int);
void push_boolean(const bool);
void push_cxx_closure(cxx_function, const int);
void push_cxx_function(cxx_function);
void push_integer(const int);
void push_nil(void);
void push_string(const std::string&);
void push_value(const int);
void raw_get(const int);
void raw_set(const int);
void set_global(const std::string&);
void set_metatable(const int);
void set_table(const int);
bool to_boolean(const int);
long to_integer(const int);
template< typename Type > Type* to_userdata(const int);
std::string to_string(const int);
int upvalue_index(const int);
};
} // namespace lutok
#endif // !defined(LUTOK_STATE_HPP)

67
contrib/lutok/state.ipp Normal file
View file

@ -0,0 +1,67 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#if !defined(LUTOK_STATE_IPP)
#define LUTOK_STATE_IPP
#include <lutok/state.hpp>
namespace lutok {
/// Wrapper around lua_newuserdata.
///
/// This allocates an object as big as the size of the provided Type.
///
/// \return The pointer to the allocated userdata object.
///
/// \warning Terminates execution if there is not enough memory.
template< typename Type >
Type*
state::new_userdata(void)
{
return static_cast< Type* >(new_userdata_voidp(sizeof(Type)));
}
/// Wrapper around lua_touserdata.
///
/// \param index The second parameter to lua_touserdata.
///
/// \return The return value of lua_touserdata.
template< typename Type >
Type*
state::to_userdata(const int index)
{
return static_cast< Type* >(to_userdata_voidp(index));
}
} // namespace lutok
#endif // !defined(LUTOK_STATE_IPP)

1164
contrib/lutok/state_test.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,141 @@
// Copyright 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of Google Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// \file test_utils.hpp
/// Utilities for tests of the lua modules.
///
/// This file is intended to be included once, and only once, for every test
/// program that needs it. All the code is herein contained to simplify the
/// dependency chain in the build rules.
#if !defined(LUTOK_TEST_UTILS_HPP)
# define LUTOK_TEST_UTILS_HPP
#else
# error "test_utils.hpp can only be included once"
#endif
#include <atf-c++.hpp>
#include "c_gate.hpp"
#include "exceptions.hpp"
#include "state.hpp"
namespace {
/// Checks that a given expression raises a particular lutok::api_error.
///
/// We cannot make any assumptions regarding the error text provided by Lua, so
/// we resort to checking only which API function raised the error (because our
/// code is the one hardcoding these strings).
///
/// \param exp_api_function The name of the Lua C API function that causes the
/// error.
/// \param statement The statement to execute.
#define REQUIRE_API_ERROR(exp_api_function, statement) \
do { \
try { \
statement; \
ATF_FAIL("api_error not raised by " #statement); \
} catch (const lutok::api_error& api_error) { \
ATF_REQUIRE_EQ(exp_api_function, api_error.api_function()); \
} \
} while (0)
/// Gets the pointer to the internal lua_State of a state object.
///
/// This is pure syntactic sugar to simplify typing in the test cases.
///
/// \param state The Lua state.
///
/// \return The internal lua_State of the input Lua state.
static inline lua_State*
raw(lutok::state& state)
{
return lutok::state_c_gate(state).c_state();
}
/// Ensures that the Lua stack maintains its original height upon exit.
///
/// Use an instance of this class to check that a piece of code does not have
/// side-effects on the Lua stack.
///
/// To be used within a test case only.
class stack_balance_checker {
/// The Lua state.
lutok::state& _state;
/// Whether to install a sentinel on the stack for balance enforcement.
bool _with_sentinel;
/// The height of the stack on creation.
unsigned int _old_count;
public:
/// Constructs a new stack balance checker.
///
/// \param state_ The Lua state to validate.
/// \param with_sentinel_ If true, insert a sentinel item into the stack and
/// validate upon exit that the item is still there. This is an attempt
/// to ensure that already-existing items are not removed from the stack
/// by the code under test.
stack_balance_checker(lutok::state& state_,
const bool with_sentinel_ = true) :
_state(state_),
_with_sentinel(with_sentinel_),
_old_count(_state.get_top())
{
if (_with_sentinel)
_state.push_integer(987654321);
}
/// Destructor for the object.
///
/// If the stack height does not match the height when the instance was
/// created, this fails the test case.
~stack_balance_checker(void)
{
if (_with_sentinel) {
if (!_state.is_number(-1) || _state.to_integer(-1) != 987654321)
ATF_FAIL("Stack corrupted: sentinel not found");
_state.pop(1);
}
unsigned int new_count = _state.get_top();
if (_old_count != new_count)
//ATF_FAIL(F("Stack not balanced: before %d, after %d") %
// _old_count % new_count);
ATF_FAIL("Stack not balanced");
}
};
} // anonymous namespace

View file

@ -205,6 +205,7 @@ SUBDIR.${MK_RADIUS_SUPPORT}+= libradius
SUBDIR.${MK_SENDMAIL}+= libmilter libsm libsmdb libsmutil
SUBDIR.${MK_TELNET}+= libtelnet
SUBDIR.${MK_TESTS_SUPPORT}+= atf
SUBDIR.${MK_TESTS_SUPPORT}.${MK_CXX}+= liblutok
SUBDIR.${MK_TESTS}+= tests
SUBDIR.${MK_UNBOUND}+= libunbound
SUBDIR.${MK_USB}+= libusbhid libusb

26
lib/liblutok/Makefile Normal file
View file

@ -0,0 +1,26 @@
# $FreeBSD$
LIB_CXX= lutok
INTERNALLIB=
LIBADD= lua
.PATH: ${SRCTOP}/contrib/lutok
SRCS= c_gate.cpp \
debug.cpp \
exceptions.cpp \
operations.cpp \
stack_cleaner.cpp \
state.cpp
CFLAGS+= -I${SRCTOP}/contrib/lutok/include \
-I${SRCTOP}/lib/liblua \
-I${SRCTOP}/contrib/lua/src
WARNS?= 3
SHLIB_MAJOR= 0
# lutok uses auto_ptr
CFLAGS+= -Wno-deprecated-declarations
.include <bsd.lib.mk>

View file

@ -42,6 +42,7 @@ _INTERNALLIBS= \
ipf \
lpr \
lua \
lutok \
netbsd \
ntp \
ntpevent \
@ -295,6 +296,7 @@ _DP_magic= z
_DP_mt= sbuf bsdxml
_DP_ldns= ssl crypto
_DP_lua= m
_DP_lutok= lua
.if ${MK_OPENSSL} != "no"
_DP_fetch= ssl crypto
.else
@ -473,6 +475,9 @@ LIBELFTC?= ${LIBELFTCDIR}/libelftc${PIE_SUFFIX}.a
LIBLUADIR= ${_LIB_OBJTOP}/lib/liblua
LIBLUA?= ${LIBLUADIR}/liblua${PIE_SUFFIX}.a
LIBLUTOKDIR= ${_LIB_OBJTOP}/lib/liblutok
LIBLUTOK?= ${LIBLUTOKDIR}/liblutok${PIE_SUFFIX}.a
LIBPEDIR= ${_LIB_OBJTOP}/lib/libpe
LIBPE?= ${LIBPEDIR}/libpe${PIE_SUFFIX}.a