“a0f74ef63f885d3a5f7588d251b2d0b93eb41492”上不存在“git@gitcode.net:paddlepaddle/DeepSpeech.git”
提交 59f67db1 编写于 作者: H Hui Zhang

paddleaudio as extenstion

上级 9762b4a3
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
build build
*output/ *output/
paddlespeech/audio/_paddleaudio.so
paddlespeech/audio/lib/libpaddleaudio.so
paddlespeech/version.py
docs/build/ docs/build/
docs/topic/ctc/warp-ctc/ docs/topic/ctc/warp-ctc/
......
cmake_minimum_required(VERSION 3.16 FATAL_ERROR) cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
# Use compiler ID "AppleClang" instead of "Clang" for XCode.
# Not setting this sometimes makes XCode C compiler gets detected as "Clang",
# even when the C++ one is detected as "AppleClang".
cmake_policy(SET CMP0010 NEW)
cmake_policy(SET CMP0025 NEW)
# Suppress warning flags in default MSVC configuration. It's not
# mandatory that we do this (and we don't if cmake is old), but it's
# nice when it's possible, and it's possible on our Windows configs.
if(NOT CMAKE_VERSION VERSION_LESS 3.15.0)
cmake_policy(SET CMP0092 NEW)
endif()
project(paddlespeech) project(paddlespeech)
# check and set CMAKE_CXX_STANDARD # check and set CMAKE_CXX_STANDARD
string(FIND "${CMAKE_CXX_FLAGS}" "-std=c++" env_cxx_standard) string(FIND "${CMAKE_CXX_FLAGS}" "-std=c++" env_cxx_standard)
if(env_cxx_standard GREATER -1)
message(
WARNING "C++ standard version definition detected in environment variable."
"paddlespeech requires -std=c++14. Please remove -std=c++ settings in your environment.")
endif()
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_VERBOSE_MAKEFILE ON)
# Options # Options
option(BUILD_SOX "Build libsox statically" ON) option(BUILD_SOX "Build libsox statically" ON)
option(BUILD_MAD "Enable libmad" ON)
option(BUILD_PADDLEAUDIO_PYTHON_EXTENSION "Build Python extension" ON)
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake")
find_package(Python3 COMPONENTS Interpreter Development)
find_package(pybind11 CONFIG)
message(STATUS "Python_INCLUDE_DIR=" ${Python_INCLUDE_DIR})
add_subdirectory(paddlespeech/audio/third_party) add_subdirectory(paddlespeech/audio/third_party)
add_subdirectory(paddlespeech/audio/src) add_subdirectory(paddlespeech/audio/src)
...@@ -14,3 +14,5 @@ ...@@ -14,3 +14,5 @@
import _locale import _locale
_locale._getdefaultlocale = (lambda *args: ['en_US', 'utf8']) _locale._getdefaultlocale = (lambda *args: ['en_US', 'utf8'])
...@@ -3,7 +3,29 @@ ...@@ -3,7 +3,29 @@
## Reference ## Reference
`csrc` code is reference of `torchaudio`. `csrc` code is reference of `torchaudio`.
```text
BSD 2-Clause License BSD 2-Clause License
Copyright (c) 2017 Facebook Inc. (Soumith Chintala), Copyright (c) [year], [fullname]
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
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 HOLDER 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.
```
\ No newline at end of file
...@@ -19,4 +19,4 @@ from . import io ...@@ -19,4 +19,4 @@ from . import io
from . import metric from . import metric
from . import sox_effects from . import sox_effects
from .backends import load from .backends import load
from .backends import save from .backends import save
\ No newline at end of file
import types
class _ClassNamespace(types.ModuleType):
def __init__(self, name):
super(_ClassNamespace, self).__init__('paddlespeech.classes' + name)
self.name = name
def __getattr__(self, attr):
proxy = None
if proxy is None:
raise RuntimeError(f'Class {self.name}.{attr} not registered!')
return proxy
class _Classes(types.ModuleType):
__file__ = '_classes.py'
def __init__(self):
super(_Classes, self).__init__('paddlespeech.classes')
def __getattr__(self, name):
namespace = _ClassNamespace(name)
setattr(self, name, namespace)
return namespace
@property
def loaded_libraries(self):
return paddlespeech.ops.loaded_libraries
def load_library(self, path):
"""
Loads a shared library from the given path into the current process.
The library being loaded may run global initialization code to register
custom classes with the PyTorch JIT runtime. This allows dynamically
loading custom classes. For this, you should compile your class
and the static registration code into a shared library object, and then
call ``torch.classes.load_library('path/to/libcustom.so')`` to load the
shared object.
After the library is loaded, it is added to the
``torch.classes.loaded_libraries`` attribute, a set that may be inspected
for the paths of all libraries loaded using this function.
Args:
path (str): A path to a shared library to load.
"""
paddlespeech.ops.load_library(path)
# The classes "namespace"
classes = _Classes()
\ No newline at end of file
import os
import warnings
from pathlib import Path
from ._internal import module_utils as _mod_utils # noqa: F401
_LIB_DIR = Path(__file__) / "lib"
def _get_lib_path(lib: str):
suffix = "pyd" if os.name == "nt" else "so"
path = _LIB_DIR / f"{lib}.{suffix}"
return path
def _load_lib(lib: str) -> bool:
"""Load extension module
Note:
In case `paddleaudio` is deployed with `pex` format, the library file
is not in a standard location.
In this case, we expect that `libpaddlleaudio` is available somewhere
in the search path of dynamic loading mechanism, so that importing
`_paddlleaudio` will have library loader find and load `libpaddlleaudio`.
This is the reason why the function should not raising an error when the library
file is not found.
Returns:
bool:
True if the library file is found AND the library loaded without failure.
False if the library file is not found (like in the case where paddlleaudio
is deployed with pex format, thus the shared library file is
in a non-standard location.).
If the library file is found but there is an issue loading the library,
(such as missing dependency) then this function raises the exception as-is.
Raises:
Exception:
If the library file is found, but there is an issue loading the library file,
(when underlying `ctype.DLL` throws an exception), this function will pass
the exception as-is, instead of catching it and returning bool.
The expected case is `OSError` thrown by `ctype.DLL` when a dynamic dependency
is not found.
This behavior was chosen because the expected failure case is not recoverable.
If a dependency is missing, then users have to install it.
"""
path = _get_lib_path(lib)
if not path.exists():
return False
paddlespeech.ops.load_library(path)
return True
_FFMPEG_INITIALIZED = False
def _init_ffmpeg():
global _FFMPEG_INITIALIZED
if _FFMPEG_INITIALIZED:
return
if not paddlespeech.ops.paddlleaudio.is_ffmpeg_available():
raise RuntimeError(
"paddlleaudio is not compiled with FFmpeg integration. Please set USE_FFMPEG=1 when compiling paddlleaudio."
)
try:
_load_lib("libpaddlleaudio_ffmpeg")
except OSError as err:
raise ImportError("FFmpeg libraries are not found. Please install FFmpeg.") from err
import paddllespeech._paddlleaudio_ffmpeg # noqa
paddlespeech.ops.paddlleaudio.ffmpeg_init()
if paddlespeech.ops.paddlleaudio.ffmpeg_get_log_level() > 8:
paddlespeech.ops.paddlleaudio.ffmpeg_set_log_level(8)
_FFMPEG_INITIALIZED = True
def _init_extension():
if not _mod_utils.is_module_available("paddlespeech._paddleaudio"):
warnings.warn("paddlespeech C++ extension is not available.")
return
_load_lib("libpaddleaudio")
# This import is for initializing the methods registered via PyBind11
# This has to happen after the base library is loaded
from paddlespeech import _paddleaudio # noqa
# Because this part is executed as part of `import torchaudio`, we ignore the
# initialization failure.
# If the FFmpeg integration is not properly initialized, then detailed error
# will be raised when client code attempts to import the dedicated feature.
try:
_init_ffmpeg()
except Exception:
pass
_init_extension()
\ No newline at end of file
import importlib.util
import warnings
from functools import wraps
from typing import Optional
def is_module_available(*modules: str) -> bool:
r"""Returns if a top-level module with :attr:`name` exists *without**
importing it. This is generally safer than try-catch block around a
`import X`. It avoids third party libraries breaking assumptions of some of
our tests, e.g., setting multiprocessing start method when imported
(see librosa/#747, torchvision/#544).
"""
return all(importlib.util.find_spec(m) is not None for m in modules)
def requires_module(*modules: str):
"""Decorate function to give error message if invoked without required optional modules.
This decorator is to give better error message to users rather
than raising ``NameError: name 'module' is not defined`` at random places.
"""
missing = [m for m in modules if not is_module_available(m)]
if not missing:
# fall through. If all the modules are available, no need to decorate
def decorator(func):
return func
else:
req = f"module: {missing[0]}" if len(missing) == 1 else f"modules: {missing}"
def decorator(func):
@wraps(func)
def wrapped(*args, **kwargs):
raise RuntimeError(f"{func.__module__}.{func.__name__} requires {req}")
return wrapped
return decorator
def deprecated(direction: str, version: Optional[str] = None):
"""Decorator to add deprecation message
Args:
direction (str): Migration steps to be given to users.
version (str or int): The version when the object will be removed
"""
def decorator(func):
@wraps(func)
def wrapped(*args, **kwargs):
message = (
f"{func.__module__}.{func.__name__} has been deprecated "
f'and will be removed from {"future" if version is None else version} release. '
f"{direction}"
)
warnings.warn(message, stacklevel=2)
return func(*args, **kwargs)
return wrapped
return decorator
def is_kaldi_available():
return is_module_available("paddlespeech"._paddleaudio") and paddlespeech.ops.paddleaudio.is_kaldi_available()
def requires_kaldi():
if is_kaldi_available():
def decorator(func):
return func
else:
def decorator(func):
@wraps(func)
def wrapped(*args, **kwargs):
raise RuntimeError(f"{func.__module__}.{func.__name__} requires kaldi")
return wrapped
return decorator
def _check_soundfile_importable():
if not is_module_available("soundfile"):
return False
try:
import soundfile # noqa: F401
return True
except Exception:
warnings.warn("Failed to import soundfile. 'soundfile' backend is not available.")
return False
_is_soundfile_importable = _check_soundfile_importable()
def is_soundfile_available():
return _is_soundfile_importable
def requires_soundfile():
if is_soundfile_available():
def decorator(func):
return func
else:
def decorator(func):
@wraps(func)
def wrapped(*args, **kwargs):
raise RuntimeError(f"{func.__module__}.{func.__name__} requires soundfile")
return wrapped
return decorator
def is_sox_available():
return is_module_available("paddlespeech._paddleaudio") and paddlespeech.ops.paddleaudio.is_sox_available()
def requires_sox():
if is_sox_available():
def decorator(func):
return func
else:
def decorator(func):
@wraps(func)
def wrapped(*args, **kwargs):
raise RuntimeError(f"{func.__module__}.{func.__name__} requires sox")
return wrapped
return
\ No newline at end of file
import contextlib
import ctypes
import sys
import os
import types
# Query `hasattr` only once.
_SET_GLOBAL_FLAGS = hasattr(sys, 'getdlopenflags') and hasattr(sys, 'setdlopenflags')
@contextlib.contextmanager
def dl_open_guard():
"""
# https://manpages.debian.org/bullseye/manpages-dev/dlopen.3.en.html
Context manager to set the RTLD_GLOBAL dynamic linker flag while we open a
shared library to load custom operators.
"""
if _SET_GLOBAL_FLAGS:
old_flags = sys.getdlopenflags()
sys.setdlopenflags(old_flags | ctypes.RTLD_GLOBAL)
yield
if _SET_GLOBAL_FLAGS:
sys.setdlopenflags(old_flags)
def resolve_library_path(path: str) -> str:
return os.path.realpath(path)
class _Ops(types.ModuleType):
__file__ = '_ops.py'
def __init__(self):
super(_Ops, self).__init__('paddlespeech.ops')
self.loaded_libraries = set()
def load_library(self, path):
"""
Loads a shared library from the given path into the current process.
This allows dynamically loading custom operators. For this,
you should compile your operator and
the static registration code into a shared library object, and then
call ``paddlespeech.ops.load_library('path/to/libcustom.so')`` to load the
shared object.
After the library is loaded, it is added to the
``paddlespeech.ops.loaded_libraries`` attribute, a set that may be inspected
for the paths of all libraries loaded using this function.
Args:
path (str): A path to a shared library to load.
"""
path = resolve_library_path(path)
with dl_open_guard():
# https://docs.python.org/3/library/ctypes.html?highlight=ctypes#loading-shared-libraries
# Import the shared library into the process, thus running its
# static (global) initialization code in order to register custom
# operators with the JIT.
ctypes.CDLL(path)
self.loaded_libraries.add(path)
# The ops "namespace"
ops = _Ops()
\ No newline at end of file
find_package(Python3 COMPONENTS Interpreter Development) if (MSVC)
find_package(pybind11 CONFIG) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
################################################################################
# libpaddleaudio
################################################################################
set(
LIBPADDLEAUDIO_SOURCES
utils.cpp
)
set(
LIBPADDLEAUDIO_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}
)
set(
LIBPADDLEAUDIO_LINK_LIBRARIES
)
set(
LIBPADDLEAUDIO_COMPILE_DEFINITIONS)
function(define_extension name sources libraries) #------------------------------------------------------------------------------#
# START OF CUSTOMIZATION LOGICS
#------------------------------------------------------------------------------#
if(BUILD_SOX)
list(
APPEND
LIBPADDLEAUDIO_LINK_LIBRARIES
libsox
)
list(
APPEND
LIBPADDLEAUDIO_SOURCES
# sox/io.cpp
# sox/utils.cpp
# sox/effects.cpp
# sox/effects_chain.cpp
# sox/types.cpp
)
list(
APPEND
LIBPADDLEAUDIO_COMPILE_DEFINITIONS
INCLUDE_SOX
)
endif()
#------------------------------------------------------------------------------#
# END OF CUSTOMIZATION LOGICS
#------------------------------------------------------------------------------#
function (define_library name source include_dirs link_libraries compile_defs)
add_library(${name} SHARED ${source})
target_include_directories(${name} PRIVATE ${include_dirs})
target_link_libraries(${name} ${link_libraries})
target_compile_definitions(${name} PRIVATE ${compile_defs})
set_target_properties(${name} PROPERTIES PREFIX "")
if (MSVC)
set_target_properties(${name} PROPERTIES SUFFIX ".pyd")
endif(MSVC)
install(
TARGETS ${name}
LIBRARY DESTINATION lib
RUNTIME DESTINATION lib # For Windows
)
endfunction()
define_library(
libpaddleaudio
"${LIBPADDLEAUDIO_SOURCES}"
"${LIBPADDLEAUDIO_INCLUDE_DIRS}"
"${LIBPADDLEAUDIO_LINK_LIBRARIES}"
"${LIBPADDLEAUDIO_COMPILE_DEFINITIONS}"
)
if (APPLE)
set(TORCHAUDIO_LIBRARY libtorchaudio CACHE INTERNAL "")
else()
set(TORCHAUDIO_LIBRARY -Wl,--no-as-needed libtorchaudio -Wl,--as-needed CACHE INTERNAL "")
endif()
################################################################################
# _paddleaudio.so
################################################################################
if (BUILD_PADDLEAUDIO_PYTHON_EXTENSION)
if (WIN32)
find_package(Python3 ${PYTHON_VERSION} EXACT COMPONENTS Development)
set(ADDITIONAL_ITEMS Python3::Python)
endif()
function(define_extension name sources include_dirs libraries definitions)
add_library(${name} SHARED ${sources}) add_library(${name} SHARED ${sources})
target_compile_definitions(${name} PRIVATE "${definitions}")
target_include_directories( target_include_directories(
${name} PRIVATE ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${Python3_INCLUDE_DIRS} ${pybind11_INCLUDE_DIR}) ${name} PRIVATE ${PROJECT_SOURCE_DIR} ${Python_INCLUDE_DIR} ${include_dirs})
target_link_libraries(${name} ${libraries}) target_link_libraries(
${name}
${libraries}
${TORCH_PYTHON_LIBRARY}
${ADDITIONAL_ITEMS}
)
set_target_properties(${name} PROPERTIES PREFIX "") set_target_properties(${name} PROPERTIES PREFIX "")
if (MSVC)
set_target_properties(${name} PROPERTIES SUFFIX ".pyd")
endif(MSVC)
if (APPLE)
# https://github.com/facebookarchive/caffe2/issues/854#issuecomment-364538485
# https://github.com/pytorch/pytorch/commit/73f6715f4725a0723d8171d3131e09ac7abf0666
set_target_properties(${name} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
endif()
install( install(
TARGETS ${name} TARGETS ${name}
LIBRARY DESTINATION . LIBRARY DESTINATION .
RUNTIME DESTINATION . # For Windows
) )
endfunction() endfunction()
set(
EXTENSION_SOURCES
pybind/pybind.cpp
)
#----------------------------------------------------------------------------#
# START OF CUSTOMIZATION LOGICS
#----------------------------------------------------------------------------#
if(BUILD_SOX) if(BUILD_SOX)
set( list(
APPEND
EXTENSION_SOURCES EXTENSION_SOURCES
pybind/pybind.cpp # pybind/sox/effects.cpp
# pybind/sox/effects_chain.cpp
pybind/sox/io.cpp pybind/sox/io.cpp
pybind/sox/utils.cpp pybind/sox/utils.cpp
) )
set(
LINK_LIBRARIES
libsox
)
define_extension(
_paddleaudio
"${EXTENSION_SOURCES}"
"${LINK_LIBRARIES}"
)
endif() endif()
#----------------------------------------------------------------------------#
add_subdirectory(pybind/kaldi_frontend) # END OF CUSTOMIZATION LOGICS
#----------------------------------------------------------------------------#
define_extension(
_paddleaudio
"${EXTENSION_SOURCES}"
""
libpaddleaudio
"${LIBPADDLEAUDIO_COMPILE_DEFINITIONS}"
)
# if(BUILD_CTC_DECODER)
# set(
# DECODER_EXTENSION_SOURCES
# decoder/bindings/pybind.cpp
# )
# define_extension(
# _paddleaudio_decoder
# "${DECODER_EXTENSION_SOURCES}"
# ""
# "libpaddleaudio_decoder"
# "${LIBPADDLEAUDIO_DECODER_DEFINITIONS}"
# )
# endif()
# if(USE_FFMPEG)
# set(
# FFMPEG_EXTENSION_SOURCES
# ffmpeg/pybind/typedefs.cpp
# ffmpeg/pybind/pybind.cpp
# ffmpeg/pybind/stream_reader.cpp
# )
# define_extension(
# _paddleaudio_ffmpeg
# "${FFMPEG_EXTENSION_SOURCES}"
# "${FFMPEG_INCLUDE_DIRS}"
# "libpaddleaudio_ffmpeg"
# "${LIBPADDLEAUDIO_DECODER_DEFINITIONS}"
# )
# endif()
endif()
\ No newline at end of file
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.
///
// optional - An implementation of std::optional with extensions
// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
//
// Documentation available at https://tl.tartanllama.xyz/
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to the
// public domain worldwide. This software is distributed without any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software. If not, see
// <http://creativecommons.org/publicdomain/zero/1.0/>.
// https://github.com/TartanLlama/optional
///
#ifndef TL_OPTIONAL_HPP
#define TL_OPTIONAL_HPP
#define TL_OPTIONAL_VERSION_MAJOR 1
#define TL_OPTIONAL_VERSION_MINOR 0
#define TL_OPTIONAL_VERSION_PATCH 0
#include <exception>
#include <functional>
#include <new>
#include <type_traits>
#include <utility>
#if (defined(_MSC_VER) && _MSC_VER == 1900)
#define TL_OPTIONAL_MSVC2015
#endif
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
!defined(__clang__))
#define TL_OPTIONAL_GCC49
#endif
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
!defined(__clang__))
#define TL_OPTIONAL_GCC54
#endif
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
!defined(__clang__))
#define TL_OPTIONAL_GCC55
#endif
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
!defined(__clang__))
// GCC < 5 doesn't support overloading on const&& for member functions
#define TL_OPTIONAL_NO_CONSTRR
// GCC < 5 doesn't support some standard C++11 type traits
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
std::has_trivial_copy_constructor<T>::value
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign<T>::value
// This one will be different for GCC 5.7 if it's ever supported
#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector
// for non-copyable types
#elif (defined(__GNUC__) && __GNUC__ < 8 && \
!defined(__clang__))
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
namespace tl {
namespace detail {
template<class T>
struct is_trivially_copy_constructible : std::is_trivially_copy_constructible<T>{};
#ifdef _GLIBCXX_VECTOR
template<class T, class A>
struct is_trivially_copy_constructible<std::vector<T,A>>
: std::is_trivially_copy_constructible<T>{};
#endif
}
}
#endif
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
tl::detail::is_trivially_copy_constructible<T>::value
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
std::is_trivially_copy_assignable<T>::value
#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
#else
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
std::is_trivially_copy_constructible<T>::value
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
std::is_trivially_copy_assignable<T>::value
#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
#endif
#if __cplusplus > 201103L
#define TL_OPTIONAL_CXX14
#endif
// constexpr implies const in C++11, not C++14
#if (__cplusplus == 201103L || defined(TL_OPTIONAL_MSVC2015) || \
defined(TL_OPTIONAL_GCC49))
#define TL_OPTIONAL_11_CONSTEXPR
#else
#define TL_OPTIONAL_11_CONSTEXPR constexpr
#endif
namespace tl {
#ifndef TL_MONOSTATE_INPLACE_MUTEX
#define TL_MONOSTATE_INPLACE_MUTEX
/// Used to represent an optional with no data; essentially a bool
class monostate {};
/// A tag type to tell optional to construct its value in-place
struct in_place_t {
explicit in_place_t() = default;
};
/// A tag to tell optional to construct its value in-place
static constexpr in_place_t in_place{};
#endif
template <class T> class optional;
namespace detail {
#ifndef TL_TRAITS_MUTEX
#define TL_TRAITS_MUTEX
// C++14-style aliases for brevity
template <class T> using remove_const_t = typename std::remove_const<T>::type;
template <class T>
using remove_reference_t = typename std::remove_reference<T>::type;
template <class T> using decay_t = typename std::decay<T>::type;
template <bool E, class T = void>
using enable_if_t = typename std::enable_if<E, T>::type;
template <bool B, class T, class F>
using conditional_t = typename std::conditional<B, T, F>::type;
// std::conjunction from C++17
template <class...> struct conjunction : std::true_type {};
template <class B> struct conjunction<B> : B {};
template <class B, class... Bs>
struct conjunction<B, Bs...>
: std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
#endif
// In C++11 mode, there's an issue in libc++'s std::mem_fn
// which results in a hard-error when using it in a noexcept expression
// in some cases. This is a check to workaround the common failing case.
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
template <class T> struct is_pointer_to_non_const_member_func : std::false_type{};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...)> : std::true_type{};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...)&> : std::true_type{};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...)&&> : std::true_type{};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...) volatile> : std::true_type{};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...) volatile&> : std::true_type{};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...) volatile&&> : std::true_type{};
template <class T> struct is_const_or_const_ref : std::false_type{};
template <class T> struct is_const_or_const_ref<T const&> : std::true_type{};
template <class T> struct is_const_or_const_ref<T const> : std::true_type{};
#endif
// std::invoke from C++17
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
template <typename Fn, typename... Args,
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value
&& is_const_or_const_ref<Args...>::value)>,
#endif
typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>,
int = 0>
constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
-> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
return std::mem_fn(f)(std::forward<Args>(args)...);
}
template <typename Fn, typename... Args,
typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
-> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
return std::forward<Fn>(f)(std::forward<Args>(args)...);
}
// std::invoke_result from C++17
template <class F, class, class... Us> struct invoke_result_impl;
template <class F, class... Us>
struct invoke_result_impl<
F, decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
Us...> {
using type = decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
};
template <class F, class... Us>
using invoke_result = invoke_result_impl<F, void, Us...>;
template <class F, class... Us>
using invoke_result_t = typename invoke_result<F, Us...>::type;
#if defined(_MSC_VER) && _MSC_VER <= 1900
// TODO make a version which works with MSVC 2015
template <class T, class U = T> struct is_swappable : std::true_type {};
template <class T, class U = T> struct is_nothrow_swappable : std::true_type {};
#else
// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
namespace swap_adl_tests {
// if swap ADL finds this then it would call std::swap otherwise (same
// signature)
struct tag {};
template <class T> tag swap(T &, T &);
template <class T, std::size_t N> tag swap(T (&a)[N], T (&b)[N]);
// helper functions to test if an unqualified swap is possible, and if it
// becomes std::swap
template <class, class> std::false_type can_swap(...) noexcept(false);
template <class T, class U,
class = decltype(swap(std::declval<T &>(), std::declval<U &>()))>
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(),
std::declval<U &>())));
template <class, class> std::false_type uses_std(...);
template <class T, class U>
std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag>
uses_std(int);
template <class T>
struct is_std_swap_noexcept
: std::integral_constant<bool,
std::is_nothrow_move_constructible<T>::value &&
std::is_nothrow_move_assignable<T>::value> {};
template <class T, std::size_t N>
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
template <class T, class U>
struct is_adl_swap_noexcept
: std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
} // namespace swap_adl_tests
template <class T, class U = T>
struct is_swappable
: std::integral_constant<
bool,
decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
(!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
(std::is_move_assignable<T>::value &&
std::is_move_constructible<T>::value))> {};
template <class T, std::size_t N>
struct is_swappable<T[N], T[N]>
: std::integral_constant<
bool,
decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
(!decltype(
detail::swap_adl_tests::uses_std<T[N], T[N]>(0))::value ||
is_swappable<T, T>::value)> {};
template <class T, class U = T>
struct is_nothrow_swappable
: std::integral_constant<
bool,
is_swappable<T, U>::value &&
((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value
&&detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
(!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
detail::swap_adl_tests::is_adl_swap_noexcept<T,
U>::value))> {
};
#endif
#endif
// std::void_t from C++17
template <class...> struct voider { using type = void; };
template <class... Ts> using void_t = typename voider<Ts...>::type;
// Trait for checking if a type is a tl::optional
template <class T> struct is_optional_impl : std::false_type {};
template <class T> struct is_optional_impl<optional<T>> : std::true_type {};
template <class T> using is_optional = is_optional_impl<decay_t<T>>;
// Change void to tl::monostate
template <class U>
using fixup_void = conditional_t<std::is_void<U>::value, monostate, U>;
template <class F, class U, class = invoke_result_t<F, U>>
using get_map_return = optional<fixup_void<invoke_result_t<F, U>>>;
// Check if invoking F for some Us returns void
template <class F, class = void, class... U> struct returns_void_impl;
template <class F, class... U>
struct returns_void_impl<F, void_t<invoke_result_t<F, U...>>, U...>
: std::is_void<invoke_result_t<F, U...>> {};
template <class F, class... U>
using returns_void = returns_void_impl<F, void, U...>;
template <class T, class... U>
using enable_if_ret_void = enable_if_t<returns_void<T &&, U...>::value>;
template <class T, class... U>
using disable_if_ret_void = enable_if_t<!returns_void<T &&, U...>::value>;
template <class T, class U>
using enable_forward_value =
detail::enable_if_t<std::is_constructible<T, U &&>::value &&
!std::is_same<detail::decay_t<U>, in_place_t>::value &&
!std::is_same<optional<T>, detail::decay_t<U>>::value>;
template <class T, class U, class Other>
using enable_from_other = detail::enable_if_t<
std::is_constructible<T, Other>::value &&
!std::is_constructible<T, optional<U> &>::value &&
!std::is_constructible<T, optional<U> &&>::value &&
!std::is_constructible<T, const optional<U> &>::value &&
!std::is_constructible<T, const optional<U> &&>::value &&
!std::is_convertible<optional<U> &, T>::value &&
!std::is_convertible<optional<U> &&, T>::value &&
!std::is_convertible<const optional<U> &, T>::value &&
!std::is_convertible<const optional<U> &&, T>::value>;
template <class T, class U>
using enable_assign_forward = detail::enable_if_t<
!std::is_same<optional<T>, detail::decay_t<U>>::value &&
!detail::conjunction<std::is_scalar<T>,
std::is_same<T, detail::decay_t<U>>>::value &&
std::is_constructible<T, U>::value && std::is_assignable<T &, U>::value>;
template <class T, class U, class Other>
using enable_assign_from_other = detail::enable_if_t<
std::is_constructible<T, Other>::value &&
std::is_assignable<T &, Other>::value &&
!std::is_constructible<T, optional<U> &>::value &&
!std::is_constructible<T, optional<U> &&>::value &&
!std::is_constructible<T, const optional<U> &>::value &&
!std::is_constructible<T, const optional<U> &&>::value &&
!std::is_convertible<optional<U> &, T>::value &&
!std::is_convertible<optional<U> &&, T>::value &&
!std::is_convertible<const optional<U> &, T>::value &&
!std::is_convertible<const optional<U> &&, T>::value &&
!std::is_assignable<T &, optional<U> &>::value &&
!std::is_assignable<T &, optional<U> &&>::value &&
!std::is_assignable<T &, const optional<U> &>::value &&
!std::is_assignable<T &, const optional<U> &&>::value>;
// The storage base manages the actual storage, and correctly propagates
// trivial destruction from T. This case is for when T is not trivially
// destructible.
template <class T, bool = ::std::is_trivially_destructible<T>::value>
struct optional_storage_base {
TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept
: m_dummy(), m_has_value(false) {}
template <class... U>
TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u)
: m_value(std::forward<U>(u)...), m_has_value(true) {}
~optional_storage_base() {
if (m_has_value) {
m_value.~T();
m_has_value = false;
}
}
struct dummy {};
union {
dummy m_dummy;
T m_value;
};
bool m_has_value;
};
// This case is for when T is trivially destructible.
template <class T> struct optional_storage_base<T, true> {
TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept
: m_dummy(), m_has_value(false) {}
template <class... U>
TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u)
: m_value(std::forward<U>(u)...), m_has_value(true) {}
// No destructor, so this class is trivially destructible
struct dummy {};
union {
dummy m_dummy;
T m_value;
};
bool m_has_value = false;
};
// This base class provides some handy member functions which can be used in
// further derived classes
template <class T> struct optional_operations_base : optional_storage_base<T> {
using optional_storage_base<T>::optional_storage_base;
void hard_reset() noexcept {
get().~T();
this->m_has_value = false;
}
template <class... Args> void construct(Args &&... args) noexcept {
new (std::addressof(this->m_value)) T(std::forward<Args>(args)...);
this->m_has_value = true;
}
template <class Opt> void assign(Opt &&rhs) {
if (this->has_value()) {
if (rhs.has_value()) {
this->m_value = std::forward<Opt>(rhs).get();
} else {
this->m_value.~T();
this->m_has_value = false;
}
}
else if (rhs.has_value()) {
construct(std::forward<Opt>(rhs).get());
}
}
bool has_value() const { return this->m_has_value; }
TL_OPTIONAL_11_CONSTEXPR T &get() & { return this->m_value; }
TL_OPTIONAL_11_CONSTEXPR const T &get() const & { return this->m_value; }
TL_OPTIONAL_11_CONSTEXPR T &&get() && { return std::move(this->m_value); }
#ifndef TL_OPTIONAL_NO_CONSTRR
constexpr const T &&get() const && { return std::move(this->m_value); }
#endif
};
// This class manages conditionally having a trivial copy constructor
// This specialization is for when T is trivially copy constructible
template <class T, bool = TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>
struct optional_copy_base : optional_operations_base<T> {
using optional_operations_base<T>::optional_operations_base;
};
// This specialization is for when T is not trivially copy constructible
template <class T>
struct optional_copy_base<T, false> : optional_operations_base<T> {
using optional_operations_base<T>::optional_operations_base;
optional_copy_base() = default;
optional_copy_base(const optional_copy_base &rhs)
: optional_operations_base<T>() {
if (rhs.has_value()) {
this->construct(rhs.get());
} else {
this->m_has_value = false;
}
}
optional_copy_base(optional_copy_base &&rhs) = default;
optional_copy_base &operator=(const optional_copy_base &rhs) = default;
optional_copy_base &operator=(optional_copy_base &&rhs) = default;
};
// This class manages conditionally having a trivial move constructor
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
// doesn't implement an analogue to std::is_trivially_move_constructible. We
// have to make do with a non-trivial move constructor even if T is trivially
// move constructible
#ifndef TL_OPTIONAL_GCC49
template <class T, bool = std::is_trivially_move_constructible<T>::value>
struct optional_move_base : optional_copy_base<T> {
using optional_copy_base<T>::optional_copy_base;
};
#else
template <class T, bool = false> struct optional_move_base;
#endif
template <class T> struct optional_move_base<T, false> : optional_copy_base<T> {
using optional_copy_base<T>::optional_copy_base;
optional_move_base() = default;
optional_move_base(const optional_move_base &rhs) = default;
optional_move_base(optional_move_base &&rhs) noexcept(
std::is_nothrow_move_constructible<T>::value) {
if (rhs.has_value()) {
this->construct(std::move(rhs.get()));
} else {
this->m_has_value = false;
}
}
optional_move_base &operator=(const optional_move_base &rhs) = default;
optional_move_base &operator=(optional_move_base &&rhs) = default;
};
// This class manages conditionally having a trivial copy assignment operator
template <class T, bool = TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) &&
TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) &&
TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T)>
struct optional_copy_assign_base : optional_move_base<T> {
using optional_move_base<T>::optional_move_base;
};
template <class T>
struct optional_copy_assign_base<T, false> : optional_move_base<T> {
using optional_move_base<T>::optional_move_base;
optional_copy_assign_base() = default;
optional_copy_assign_base(const optional_copy_assign_base &rhs) = default;
optional_copy_assign_base(optional_copy_assign_base &&rhs) = default;
optional_copy_assign_base &operator=(const optional_copy_assign_base &rhs) {
this->assign(rhs);
return *this;
}
optional_copy_assign_base &
operator=(optional_copy_assign_base &&rhs) = default;
};
// This class manages conditionally having a trivial move assignment operator
// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
// doesn't implement an analogue to std::is_trivially_move_assignable. We have
// to make do with a non-trivial move assignment operator even if T is trivially
// move assignable
#ifndef TL_OPTIONAL_GCC49
template <class T, bool = std::is_trivially_destructible<T>::value
&&std::is_trivially_move_constructible<T>::value
&&std::is_trivially_move_assignable<T>::value>
struct optional_move_assign_base : optional_copy_assign_base<T> {
using optional_copy_assign_base<T>::optional_copy_assign_base;
};
#else
template <class T, bool = false> struct optional_move_assign_base;
#endif
template <class T>
struct optional_move_assign_base<T, false> : optional_copy_assign_base<T> {
using optional_copy_assign_base<T>::optional_copy_assign_base;
optional_move_assign_base() = default;
optional_move_assign_base(const optional_move_assign_base &rhs) = default;
optional_move_assign_base(optional_move_assign_base &&rhs) = default;
optional_move_assign_base &
operator=(const optional_move_assign_base &rhs) = default;
optional_move_assign_base &
operator=(optional_move_assign_base &&rhs) noexcept(
std::is_nothrow_move_constructible<T>::value
&&std::is_nothrow_move_assignable<T>::value) {
this->assign(std::move(rhs));
return *this;
}
};
// optional_delete_ctor_base will conditionally delete copy and move
// constructors depending on whether T is copy/move constructible
template <class T, bool EnableCopy = std::is_copy_constructible<T>::value,
bool EnableMove = std::is_move_constructible<T>::value>
struct optional_delete_ctor_base {
optional_delete_ctor_base() = default;
optional_delete_ctor_base(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default;
optional_delete_ctor_base &
operator=(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base &
operator=(optional_delete_ctor_base &&) noexcept = default;
};
template <class T> struct optional_delete_ctor_base<T, true, false> {
optional_delete_ctor_base() = default;
optional_delete_ctor_base(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete;
optional_delete_ctor_base &
operator=(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base &
operator=(optional_delete_ctor_base &&) noexcept = default;
};
template <class T> struct optional_delete_ctor_base<T, false, true> {
optional_delete_ctor_base() = default;
optional_delete_ctor_base(const optional_delete_ctor_base &) = delete;
optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default;
optional_delete_ctor_base &
operator=(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base &
operator=(optional_delete_ctor_base &&) noexcept = default;
};
template <class T> struct optional_delete_ctor_base<T, false, false> {
optional_delete_ctor_base() = default;
optional_delete_ctor_base(const optional_delete_ctor_base &) = delete;
optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete;
optional_delete_ctor_base &
operator=(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base &
operator=(optional_delete_ctor_base &&) noexcept = default;
};
// optional_delete_assign_base will conditionally delete copy and move
// constructors depending on whether T is copy/move constructible + assignable
template <class T,
bool EnableCopy = (std::is_copy_constructible<T>::value &&
std::is_copy_assignable<T>::value),
bool EnableMove = (std::is_move_constructible<T>::value &&
std::is_move_assignable<T>::value)>
struct optional_delete_assign_base {
optional_delete_assign_base() = default;
optional_delete_assign_base(const optional_delete_assign_base &) = default;
optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
default;
optional_delete_assign_base &
operator=(const optional_delete_assign_base &) = default;
optional_delete_assign_base &
operator=(optional_delete_assign_base &&) noexcept = default;
};
template <class T> struct optional_delete_assign_base<T, true, false> {
optional_delete_assign_base() = default;
optional_delete_assign_base(const optional_delete_assign_base &) = default;
optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
default;
optional_delete_assign_base &
operator=(const optional_delete_assign_base &) = default;
optional_delete_assign_base &
operator=(optional_delete_assign_base &&) noexcept = delete;
};
template <class T> struct optional_delete_assign_base<T, false, true> {
optional_delete_assign_base() = default;
optional_delete_assign_base(const optional_delete_assign_base &) = default;
optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
default;
optional_delete_assign_base &
operator=(const optional_delete_assign_base &) = delete;
optional_delete_assign_base &
operator=(optional_delete_assign_base &&) noexcept = default;
};
template <class T> struct optional_delete_assign_base<T, false, false> {
optional_delete_assign_base() = default;
optional_delete_assign_base(const optional_delete_assign_base &) = default;
optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
default;
optional_delete_assign_base &
operator=(const optional_delete_assign_base &) = delete;
optional_delete_assign_base &
operator=(optional_delete_assign_base &&) noexcept = delete;
};
} // namespace detail
/// A tag type to represent an empty optional
struct nullopt_t {
struct do_not_use {};
constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept {}
};
/// Represents an empty optional
static constexpr nullopt_t nullopt{nullopt_t::do_not_use{},
nullopt_t::do_not_use{}};
class bad_optional_access : public std::exception {
public:
bad_optional_access() = default;
const char *what() const noexcept { return "Optional has no value"; }
};
/// An optional object is an object that contains the storage for another
/// object and manages the lifetime of this contained object, if any. The
/// contained object may be initialized after the optional object has been
/// initialized, and may be destroyed before the optional object has been
/// destroyed. The initialization state of the contained object is tracked by
/// the optional object.
template <class T>
class optional : private detail::optional_move_assign_base<T>,
private detail::optional_delete_ctor_base<T>,
private detail::optional_delete_assign_base<T> {
using base = detail::optional_move_assign_base<T>;
static_assert(!std::is_same<T, in_place_t>::value,
"instantiation of optional with in_place_t is ill-formed");
static_assert(!std::is_same<detail::decay_t<T>, nullopt_t>::value,
"instantiation of optional with nullopt_t is ill-formed");
public:
// The different versions for C++14 and 11 are needed because deduced return
// types are not SFINAE-safe. This provides better support for things like
// generic lambdas. C.f.
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html
#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
!defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
/// Carries out some operation which returns an optional on the stored
/// object if there is one.
template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & {
using result = detail::invoke_result_t<F, T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && {
using result = detail::invoke_result_t<F, T &&>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: result(nullopt);
}
template <class F> constexpr auto and_then(F &&f) const & {
using result = detail::invoke_result_t<F, const T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F> constexpr auto and_then(F &&f) const && {
using result = detail::invoke_result_t<F, const T &&>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: result(nullopt);
}
#endif
#else
/// Carries out some operation which returns an optional on the stored
/// object if there is one.
template <class F>
TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &> and_then(F &&f) & {
using result = detail::invoke_result_t<F, T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
template <class F>
TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &&> and_then(F &&f) && {
using result = detail::invoke_result_t<F, T &&>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: result(nullopt);
}
template <class F>
constexpr detail::invoke_result_t<F, const T &> and_then(F &&f) const & {
using result = detail::invoke_result_t<F, const T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F>
constexpr detail::invoke_result_t<F, const T &&> and_then(F &&f) const && {
using result = detail::invoke_result_t<F, const T &&>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: result(nullopt);
}
#endif
#endif
#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
!defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
/// Carries out some operation on the stored object if there is one.
template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & {
return optional_map_impl(*this, std::forward<F>(f));
}
template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && {
return optional_map_impl(std::move(*this), std::forward<F>(f));
}
template <class F> constexpr auto map(F &&f) const & {
return optional_map_impl(*this, std::forward<F>(f));
}
template <class F> constexpr auto map(F &&f) const && {
return optional_map_impl(std::move(*this), std::forward<F>(f));
}
#else
/// Carries out some operation on the stored object if there is one.
template <class F>
TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional &>(),
std::declval<F &&>()))
map(F &&f) & {
return optional_map_impl(*this, std::forward<F>(f));
}
template <class F>
TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional &&>(),
std::declval<F &&>()))
map(F &&f) && {
return optional_map_impl(std::move(*this), std::forward<F>(f));
}
template <class F>
constexpr decltype(optional_map_impl(std::declval<const optional &>(),
std::declval<F &&>()))
map(F &&f) const & {
return optional_map_impl(*this, std::forward<F>(f));
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F>
constexpr decltype(optional_map_impl(std::declval<const optional &&>(),
std::declval<F &&>()))
map(F &&f) const && {
return optional_map_impl(std::move(*this), std::forward<F>(f));
}
#endif
#endif
#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
!defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
/// Carries out some operation on the stored object if there is one.
template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) & {
return optional_map_impl(*this, std::forward<F>(f));
}
template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) && {
return optional_map_impl(std::move(*this), std::forward<F>(f));
}
template <class F> constexpr auto transform(F&& f) const & {
return optional_map_impl(*this, std::forward<F>(f));
}
template <class F> constexpr auto transform(F&& f) const && {
return optional_map_impl(std::move(*this), std::forward<F>(f));
}
#else
/// Carries out some operation on the stored object if there is one.
template <class F>
TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&>(),
std::declval<F&&>()))
transform(F&& f) & {
return optional_map_impl(*this, std::forward<F>(f));
}
template <class F>
TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&&>(),
std::declval<F&&>()))
transform(F&& f) && {
return optional_map_impl(std::move(*this), std::forward<F>(f));
}
template <class F>
constexpr decltype(optional_map_impl(std::declval<const optional&>(),
std::declval<F&&>()))
transform(F&& f) const & {
return optional_map_impl(*this, std::forward<F>(f));
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F>
constexpr decltype(optional_map_impl(std::declval<const optional&&>(),
std::declval<F&&>()))
transform(F&& f) const && {
return optional_map_impl(std::move(*this), std::forward<F>(f));
}
#endif
#endif
/// Calls `f` if the optional is empty
template <class F, detail::enable_if_ret_void<F> * = nullptr>
optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & {
if (has_value())
return *this;
std::forward<F>(f)();
return nullopt;
}
template <class F, detail::disable_if_ret_void<F> * = nullptr>
optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & {
return has_value() ? *this : std::forward<F>(f)();
}
template <class F, detail::enable_if_ret_void<F> * = nullptr>
optional<T> or_else(F &&f) && {
if (has_value())
return std::move(*this);
std::forward<F>(f)();
return nullopt;
}
template <class F, detail::disable_if_ret_void<F> * = nullptr>
optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && {
return has_value() ? std::move(*this) : std::forward<F>(f)();
}
template <class F, detail::enable_if_ret_void<F> * = nullptr>
optional<T> or_else(F &&f) const & {
if (has_value())
return *this;
std::forward<F>(f)();
return nullopt;
}
template <class F, detail::disable_if_ret_void<F> * = nullptr>
optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & {
return has_value() ? *this : std::forward<F>(f)();
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F, detail::enable_if_ret_void<F> * = nullptr>
optional<T> or_else(F &&f) const && {
if (has_value())
return std::move(*this);
std::forward<F>(f)();
return nullopt;
}
template <class F, detail::disable_if_ret_void<F> * = nullptr>
optional<T> or_else(F &&f) const && {
return has_value() ? std::move(*this) : std::forward<F>(f)();
}
#endif
/// Maps the stored value with `f` if there is one, otherwise returns `u`.
template <class F, class U> U map_or(F &&f, U &&u) & {
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: std::forward<U>(u);
}
template <class F, class U> U map_or(F &&f, U &&u) && {
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: std::forward<U>(u);
}
template <class F, class U> U map_or(F &&f, U &&u) const & {
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: std::forward<U>(u);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F, class U> U map_or(F &&f, U &&u) const && {
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: std::forward<U>(u);
}
#endif
/// Maps the stored value with `f` if there is one, otherwise calls
/// `u` and returns the result.
template <class F, class U>
detail::invoke_result_t<U> map_or_else(F &&f, U &&u) & {
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: std::forward<U>(u)();
}
template <class F, class U>
detail::invoke_result_t<U> map_or_else(F &&f, U &&u) && {
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: std::forward<U>(u)();
}
template <class F, class U>
detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const & {
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: std::forward<U>(u)();
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F, class U>
detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const && {
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: std::forward<U>(u)();
}
#endif
/// Returns `u` if `*this` has a value, otherwise an empty optional.
template <class U>
constexpr optional<typename std::decay<U>::type> conjunction(U &&u) const {
using result = optional<detail::decay_t<U>>;
return has_value() ? result{u} : result{nullopt};
}
/// Returns `rhs` if `*this` is empty, otherwise the current value.
TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & {
return has_value() ? *this : rhs;
}
constexpr optional disjunction(const optional &rhs) const & {
return has_value() ? *this : rhs;
}
TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && {
return has_value() ? std::move(*this) : rhs;
}
#ifndef TL_OPTIONAL_NO_CONSTRR
constexpr optional disjunction(const optional &rhs) const && {
return has_value() ? std::move(*this) : rhs;
}
#endif
TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & {
return has_value() ? *this : std::move(rhs);
}
constexpr optional disjunction(optional &&rhs) const & {
return has_value() ? *this : std::move(rhs);
}
TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && {
return has_value() ? std::move(*this) : std::move(rhs);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
constexpr optional disjunction(optional &&rhs) const && {
return has_value() ? std::move(*this) : std::move(rhs);
}
#endif
/// Takes the value out of the optional, leaving it empty
optional take() {
optional ret = std::move(*this);
reset();
return ret;
}
using value_type = T;
/// Constructs an optional that does not contain a value.
constexpr optional() noexcept = default;
constexpr optional(nullopt_t) noexcept {}
/// Copy constructor
///
/// If `rhs` contains a value, the stored value is direct-initialized with
/// it. Otherwise, the constructed optional is empty.
TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) = default;
/// Move constructor
///
/// If `rhs` contains a value, the stored value is direct-initialized with
/// it. Otherwise, the constructed optional is empty.
TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default;
/// Constructs the stored value in-place using the given arguments.
template <class... Args>
constexpr explicit optional(
detail::enable_if_t<std::is_constructible<T, Args...>::value, in_place_t>,
Args &&... args)
: base(in_place, std::forward<Args>(args)...) {}
template <class U, class... Args>
TL_OPTIONAL_11_CONSTEXPR explicit optional(
detail::enable_if_t<std::is_constructible<T, std::initializer_list<U> &,
Args &&...>::value,
in_place_t>,
std::initializer_list<U> il, Args &&... args) {
this->construct(il, std::forward<Args>(args)...);
}
/// Constructs the stored value with `u`.
template <
class U = T,
detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
detail::enable_forward_value<T, U> * = nullptr>
constexpr optional(U &&u) : base(in_place, std::forward<U>(u)) {}
template <
class U = T,
detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
detail::enable_forward_value<T, U> * = nullptr>
constexpr explicit optional(U &&u) : base(in_place, std::forward<U>(u)) {}
/// Converting copy constructor.
template <
class U, detail::enable_from_other<T, U, const U &> * = nullptr,
detail::enable_if_t<std::is_convertible<const U &, T>::value> * = nullptr>
optional(const optional<U> &rhs) {
if (rhs.has_value()) {
this->construct(*rhs);
}
}
template <class U, detail::enable_from_other<T, U, const U &> * = nullptr,
detail::enable_if_t<!std::is_convertible<const U &, T>::value> * =
nullptr>
explicit optional(const optional<U> &rhs) {
if (rhs.has_value()) {
this->construct(*rhs);
}
}
/// Converting move constructor.
template <
class U, detail::enable_from_other<T, U, U &&> * = nullptr,
detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr>
optional(optional<U> &&rhs) {
if (rhs.has_value()) {
this->construct(std::move(*rhs));
}
}
template <
class U, detail::enable_from_other<T, U, U &&> * = nullptr,
detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr>
explicit optional(optional<U> &&rhs) {
if (rhs.has_value()) {
this->construct(std::move(*rhs));
}
}
/// Destroys the stored value if there is one.
~optional() = default;
/// Assignment to empty.
///
/// Destroys the current value if there is one.
optional &operator=(nullopt_t) noexcept {
if (has_value()) {
this->m_value.~T();
this->m_has_value = false;
}
return *this;
}
/// Copy assignment.
///
/// Copies the value from `rhs` if there is one. Otherwise resets the stored
/// value in `*this`.
optional &operator=(const optional &rhs) = default;
/// Move assignment.
///
/// Moves the value from `rhs` if there is one. Otherwise resets the stored
/// value in `*this`.
optional &operator=(optional &&rhs) = default;
/// Assigns the stored value from `u`, destroying the old value if there was
/// one.
template <class U = T, detail::enable_assign_forward<T, U> * = nullptr>
optional &operator=(U &&u) {
if (has_value()) {
this->m_value = std::forward<U>(u);
} else {
this->construct(std::forward<U>(u));
}
return *this;
}
/// Converting copy assignment operator.
///
/// Copies the value from `rhs` if there is one. Otherwise resets the stored
/// value in `*this`.
template <class U,
detail::enable_assign_from_other<T, U, const U &> * = nullptr>
optional &operator=(const optional<U> &rhs) {
if (has_value()) {
if (rhs.has_value()) {
this->m_value = *rhs;
} else {
this->hard_reset();
}
}
if (rhs.has_value()) {
this->construct(*rhs);
}
return *this;
}
// TODO check exception guarantee
/// Converting move assignment operator.
///
/// Moves the value from `rhs` if there is one. Otherwise resets the stored
/// value in `*this`.
template <class U, detail::enable_assign_from_other<T, U, U> * = nullptr>
optional &operator=(optional<U> &&rhs) {
if (has_value()) {
if (rhs.has_value()) {
this->m_value = std::move(*rhs);
} else {
this->hard_reset();
}
}
if (rhs.has_value()) {
this->construct(std::move(*rhs));
}
return *this;
}
/// Constructs the value in-place, destroying the current one if there is
/// one.
template <class... Args> T &emplace(Args &&... args) {
static_assert(std::is_constructible<T, Args &&...>::value,
"T must be constructible with Args");
*this = nullopt;
this->construct(std::forward<Args>(args)...);
return value();
}
template <class U, class... Args>
detail::enable_if_t<
std::is_constructible<T, std::initializer_list<U> &, Args &&...>::value,
T &>
emplace(std::initializer_list<U> il, Args &&... args) {
*this = nullopt;
this->construct(il, std::forward<Args>(args)...);
return value();
}
/// Swaps this optional with the other.
///
/// If neither optionals have a value, nothing happens.
/// If both have a value, the values are swapped.
/// If one has a value, it is moved to the other and the movee is left
/// valueless.
void
swap(optional &rhs) noexcept(std::is_nothrow_move_constructible<T>::value
&&detail::is_nothrow_swappable<T>::value) {
using std::swap;
if (has_value()) {
if (rhs.has_value()) {
swap(**this, *rhs);
} else {
new (std::addressof(rhs.m_value)) T(std::move(this->m_value));
this->m_value.T::~T();
}
} else if (rhs.has_value()) {
new (std::addressof(this->m_value)) T(std::move(rhs.m_value));
rhs.m_value.T::~T();
}
swap(this->m_has_value, rhs.m_has_value);
}
/// Returns a pointer to the stored value
constexpr const T *operator->() const {
return std::addressof(this->m_value);
}
TL_OPTIONAL_11_CONSTEXPR T *operator->() {
return std::addressof(this->m_value);
}
/// Returns the stored value
TL_OPTIONAL_11_CONSTEXPR T &operator*() & { return this->m_value; }
constexpr const T &operator*() const & { return this->m_value; }
TL_OPTIONAL_11_CONSTEXPR T &&operator*() && {
return std::move(this->m_value);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
constexpr const T &&operator*() const && { return std::move(this->m_value); }
#endif
/// Returns whether or not the optional has a value
constexpr bool has_value() const noexcept { return this->m_has_value; }
constexpr explicit operator bool() const noexcept {
return this->m_has_value;
}
/// Returns the contained value if there is one, otherwise throws bad_optional_access
TL_OPTIONAL_11_CONSTEXPR T &value() & {
if (has_value())
return this->m_value;
throw bad_optional_access();
}
TL_OPTIONAL_11_CONSTEXPR const T &value() const & {
if (has_value())
return this->m_value;
throw bad_optional_access();
}
TL_OPTIONAL_11_CONSTEXPR T &&value() && {
if (has_value())
return std::move(this->m_value);
throw bad_optional_access();
}
#ifndef TL_OPTIONAL_NO_CONSTRR
TL_OPTIONAL_11_CONSTEXPR const T &&value() const && {
if (has_value())
return std::move(this->m_value);
throw bad_optional_access();
}
#endif
/// Returns the stored value if there is one, otherwise returns `u`
template <class U> constexpr T value_or(U &&u) const & {
static_assert(std::is_copy_constructible<T>::value &&
std::is_convertible<U &&, T>::value,
"T must be copy constructible and convertible from U");
return has_value() ? **this : static_cast<T>(std::forward<U>(u));
}
template <class U> TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && {
static_assert(std::is_move_constructible<T>::value &&
std::is_convertible<U &&, T>::value,
"T must be move constructible and convertible from U");
return has_value() ? **this : static_cast<T>(std::forward<U>(u));
}
/// Destroys the stored value if one exists, making the optional empty
void reset() noexcept {
if (has_value()) {
this->m_value.~T();
this->m_has_value = false;
}
}
}; // namespace tl
/// Compares two optional objects
template <class T, class U>
inline constexpr bool operator==(const optional<T> &lhs,
const optional<U> &rhs) {
return lhs.has_value() == rhs.has_value() &&
(!lhs.has_value() || *lhs == *rhs);
}
template <class T, class U>
inline constexpr bool operator!=(const optional<T> &lhs,
const optional<U> &rhs) {
return lhs.has_value() != rhs.has_value() ||
(lhs.has_value() && *lhs != *rhs);
}
template <class T, class U>
inline constexpr bool operator<(const optional<T> &lhs,
const optional<U> &rhs) {
return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs);
}
template <class T, class U>
inline constexpr bool operator>(const optional<T> &lhs,
const optional<U> &rhs) {
return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs);
}
template <class T, class U>
inline constexpr bool operator<=(const optional<T> &lhs,
const optional<U> &rhs) {
return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs);
}
template <class T, class U>
inline constexpr bool operator>=(const optional<T> &lhs,
const optional<U> &rhs) {
return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs);
}
/// Compares an optional to a `nullopt`
template <class T>
inline constexpr bool operator==(const optional<T> &lhs, nullopt_t) noexcept {
return !lhs.has_value();
}
template <class T>
inline constexpr bool operator==(nullopt_t, const optional<T> &rhs) noexcept {
return !rhs.has_value();
}
template <class T>
inline constexpr bool operator!=(const optional<T> &lhs, nullopt_t) noexcept {
return lhs.has_value();
}
template <class T>
inline constexpr bool operator!=(nullopt_t, const optional<T> &rhs) noexcept {
return rhs.has_value();
}
template <class T>
inline constexpr bool operator<(const optional<T> &, nullopt_t) noexcept {
return false;
}
template <class T>
inline constexpr bool operator<(nullopt_t, const optional<T> &rhs) noexcept {
return rhs.has_value();
}
template <class T>
inline constexpr bool operator<=(const optional<T> &lhs, nullopt_t) noexcept {
return !lhs.has_value();
}
template <class T>
inline constexpr bool operator<=(nullopt_t, const optional<T> &) noexcept {
return true;
}
template <class T>
inline constexpr bool operator>(const optional<T> &lhs, nullopt_t) noexcept {
return lhs.has_value();
}
template <class T>
inline constexpr bool operator>(nullopt_t, const optional<T> &) noexcept {
return false;
}
template <class T>
inline constexpr bool operator>=(const optional<T> &, nullopt_t) noexcept {
return true;
}
template <class T>
inline constexpr bool operator>=(nullopt_t, const optional<T> &rhs) noexcept {
return !rhs.has_value();
}
/// Compares the optional with a value.
template <class T, class U>
inline constexpr bool operator==(const optional<T> &lhs, const U &rhs) {
return lhs.has_value() ? *lhs == rhs : false;
}
template <class T, class U>
inline constexpr bool operator==(const U &lhs, const optional<T> &rhs) {
return rhs.has_value() ? lhs == *rhs : false;
}
template <class T, class U>
inline constexpr bool operator!=(const optional<T> &lhs, const U &rhs) {
return lhs.has_value() ? *lhs != rhs : true;
}
template <class T, class U>
inline constexpr bool operator!=(const U &lhs, const optional<T> &rhs) {
return rhs.has_value() ? lhs != *rhs : true;
}
template <class T, class U>
inline constexpr bool operator<(const optional<T> &lhs, const U &rhs) {
return lhs.has_value() ? *lhs < rhs : true;
}
template <class T, class U>
inline constexpr bool operator<(const U &lhs, const optional<T> &rhs) {
return rhs.has_value() ? lhs < *rhs : false;
}
template <class T, class U>
inline constexpr bool operator<=(const optional<T> &lhs, const U &rhs) {
return lhs.has_value() ? *lhs <= rhs : true;
}
template <class T, class U>
inline constexpr bool operator<=(const U &lhs, const optional<T> &rhs) {
return rhs.has_value() ? lhs <= *rhs : false;
}
template <class T, class U>
inline constexpr bool operator>(const optional<T> &lhs, const U &rhs) {
return lhs.has_value() ? *lhs > rhs : false;
}
template <class T, class U>
inline constexpr bool operator>(const U &lhs, const optional<T> &rhs) {
return rhs.has_value() ? lhs > *rhs : true;
}
template <class T, class U>
inline constexpr bool operator>=(const optional<T> &lhs, const U &rhs) {
return lhs.has_value() ? *lhs >= rhs : false;
}
template <class T, class U>
inline constexpr bool operator>=(const U &lhs, const optional<T> &rhs) {
return rhs.has_value() ? lhs >= *rhs : true;
}
template <class T,
detail::enable_if_t<std::is_move_constructible<T>::value> * = nullptr,
detail::enable_if_t<detail::is_swappable<T>::value> * = nullptr>
void swap(optional<T> &lhs,
optional<T> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
return lhs.swap(rhs);
}
namespace detail {
struct i_am_secret {};
} // namespace detail
template <class T = detail::i_am_secret, class U,
class Ret =
detail::conditional_t<std::is_same<T, detail::i_am_secret>::value,
detail::decay_t<U>, T>>
inline constexpr optional<Ret> make_optional(U &&v) {
return optional<Ret>(std::forward<U>(v));
}
template <class T, class... Args>
inline constexpr optional<T> make_optional(Args &&... args) {
return optional<T>(in_place, std::forward<Args>(args)...);
}
template <class T, class U, class... Args>
inline constexpr optional<T> make_optional(std::initializer_list<U> il,
Args &&... args) {
return optional<T>(in_place, il, std::forward<Args>(args)...);
}
#if __cplusplus >= 201703L
template <class T> optional(T)->optional<T>;
#endif
/// \exclude
namespace detail {
#ifdef TL_OPTIONAL_CXX14
template <class Opt, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Opt>())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
constexpr auto optional_map_impl(Opt &&opt, F &&f) {
return opt.has_value()
? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt))
: optional<Ret>(nullopt);
}
template <class Opt, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Opt>())),
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
auto optional_map_impl(Opt &&opt, F &&f) {
if (opt.has_value()) {
detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt));
return make_optional(monostate{});
}
return optional<monostate>(nullopt);
}
#else
template <class Opt, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Opt>())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
constexpr auto optional_map_impl(Opt &&opt, F &&f) -> optional<Ret> {
return opt.has_value()
? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt))
: optional<Ret>(nullopt);
}
template <class Opt, class F,
class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Opt>())),
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
auto optional_map_impl(Opt &&opt, F &&f) -> optional<monostate> {
if (opt.has_value()) {
detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt));
return monostate{};
}
return nullopt;
}
#endif
} // namespace detail
/// Specialization for when `T` is a reference. `optional<T&>` acts similarly
/// to a `T*`, but provides more operations and shows intent more clearly.
template <class T> class optional<T &> {
public:
// The different versions for C++14 and 11 are needed because deduced return
// types are not SFINAE-safe. This provides better support for things like
// generic lambdas. C.f.
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html
#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
!defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
/// Carries out some operation which returns an optional on the stored
/// object if there is one.
template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & {
using result = detail::invoke_result_t<F, T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && {
using result = detail::invoke_result_t<F, T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
template <class F> constexpr auto and_then(F &&f) const & {
using result = detail::invoke_result_t<F, const T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F> constexpr auto and_then(F &&f) const && {
using result = detail::invoke_result_t<F, const T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
#endif
#else
/// Carries out some operation which returns an optional on the stored
/// object if there is one.
template <class F>
TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &> and_then(F &&f) & {
using result = detail::invoke_result_t<F, T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
template <class F>
TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &> and_then(F &&f) && {
using result = detail::invoke_result_t<F, T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
template <class F>
constexpr detail::invoke_result_t<F, const T &> and_then(F &&f) const & {
using result = detail::invoke_result_t<F, const T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F>
constexpr detail::invoke_result_t<F, const T &> and_then(F &&f) const && {
using result = detail::invoke_result_t<F, const T &>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: result(nullopt);
}
#endif
#endif
#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
!defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
/// Carries out some operation on the stored object if there is one.
template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & {
return detail::optional_map_impl(*this, std::forward<F>(f));
}
template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && {
return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
}
template <class F> constexpr auto map(F &&f) const & {
return detail::optional_map_impl(*this, std::forward<F>(f));
}
template <class F> constexpr auto map(F &&f) const && {
return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
}
#else
/// Carries out some operation on the stored object if there is one.
template <class F>
TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional &>(),
std::declval<F &&>()))
map(F &&f) & {
return detail::optional_map_impl(*this, std::forward<F>(f));
}
template <class F>
TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional &&>(),
std::declval<F &&>()))
map(F &&f) && {
return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
}
template <class F>
constexpr decltype(detail::optional_map_impl(std::declval<const optional &>(),
std::declval<F &&>()))
map(F &&f) const & {
return detail::optional_map_impl(*this, std::forward<F>(f));
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F>
constexpr decltype(detail::optional_map_impl(std::declval<const optional &&>(),
std::declval<F &&>()))
map(F &&f) const && {
return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
}
#endif
#endif
#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
!defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
/// Carries out some operation on the stored object if there is one.
template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) & {
return detail::optional_map_impl(*this, std::forward<F>(f));
}
template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) && {
return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
}
template <class F> constexpr auto transform(F&& f) const & {
return detail::optional_map_impl(*this, std::forward<F>(f));
}
template <class F> constexpr auto transform(F&& f) const && {
return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
}
#else
/// Carries out some operation on the stored object if there is one.
template <class F>
TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional&>(),
std::declval<F&&>()))
transform(F&& f) & {
return detail::optional_map_impl(*this, std::forward<F>(f));
}
/// \group map
/// \synopsis template <class F> auto transform(F &&f) &&;
template <class F>
TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional&&>(),
std::declval<F&&>()))
transform(F&& f) && {
return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
}
template <class F>
constexpr decltype(detail::optional_map_impl(std::declval<const optional&>(),
std::declval<F&&>()))
transform(F&& f) const & {
return detail::optional_map_impl(*this, std::forward<F>(f));
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F>
constexpr decltype(detail::optional_map_impl(std::declval<const optional&&>(),
std::declval<F&&>()))
transform(F&& f) const && {
return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
}
#endif
#endif
/// Calls `f` if the optional is empty
template <class F, detail::enable_if_ret_void<F> * = nullptr>
optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & {
if (has_value())
return *this;
std::forward<F>(f)();
return nullopt;
}
template <class F, detail::disable_if_ret_void<F> * = nullptr>
optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & {
return has_value() ? *this : std::forward<F>(f)();
}
template <class F, detail::enable_if_ret_void<F> * = nullptr>
optional<T> or_else(F &&f) && {
if (has_value())
return std::move(*this);
std::forward<F>(f)();
return nullopt;
}
template <class F, detail::disable_if_ret_void<F> * = nullptr>
optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && {
return has_value() ? std::move(*this) : std::forward<F>(f)();
}
template <class F, detail::enable_if_ret_void<F> * = nullptr>
optional<T> or_else(F &&f) const & {
if (has_value())
return *this;
std::forward<F>(f)();
return nullopt;
}
template <class F, detail::disable_if_ret_void<F> * = nullptr>
optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & {
return has_value() ? *this : std::forward<F>(f)();
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F, detail::enable_if_ret_void<F> * = nullptr>
optional<T> or_else(F &&f) const && {
if (has_value())
return std::move(*this);
std::forward<F>(f)();
return nullopt;
}
template <class F, detail::disable_if_ret_void<F> * = nullptr>
optional<T> or_else(F &&f) const && {
return has_value() ? std::move(*this) : std::forward<F>(f)();
}
#endif
/// Maps the stored value with `f` if there is one, otherwise returns `u`
template <class F, class U> U map_or(F &&f, U &&u) & {
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: std::forward<U>(u);
}
template <class F, class U> U map_or(F &&f, U &&u) && {
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: std::forward<U>(u);
}
template <class F, class U> U map_or(F &&f, U &&u) const & {
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: std::forward<U>(u);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F, class U> U map_or(F &&f, U &&u) const && {
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: std::forward<U>(u);
}
#endif
/// Maps the stored value with `f` if there is one, otherwise calls
/// `u` and returns the result.
template <class F, class U>
detail::invoke_result_t<U> map_or_else(F &&f, U &&u) & {
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: std::forward<U>(u)();
}
template <class F, class U>
detail::invoke_result_t<U> map_or_else(F &&f, U &&u) && {
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: std::forward<U>(u)();
}
template <class F, class U>
detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const & {
return has_value() ? detail::invoke(std::forward<F>(f), **this)
: std::forward<U>(u)();
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template <class F, class U>
detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const && {
return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
: std::forward<U>(u)();
}
#endif
/// Returns `u` if `*this` has a value, otherwise an empty optional.
template <class U>
constexpr optional<typename std::decay<U>::type> conjunction(U &&u) const {
using result = optional<detail::decay_t<U>>;
return has_value() ? result{u} : result{nullopt};
}
/// Returns `rhs` if `*this` is empty, otherwise the current value.
TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & {
return has_value() ? *this : rhs;
}
constexpr optional disjunction(const optional &rhs) const & {
return has_value() ? *this : rhs;
}
TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && {
return has_value() ? std::move(*this) : rhs;
}
#ifndef TL_OPTIONAL_NO_CONSTRR
constexpr optional disjunction(const optional &rhs) const && {
return has_value() ? std::move(*this) : rhs;
}
#endif
TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & {
return has_value() ? *this : std::move(rhs);
}
constexpr optional disjunction(optional &&rhs) const & {
return has_value() ? *this : std::move(rhs);
}
TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && {
return has_value() ? std::move(*this) : std::move(rhs);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
constexpr optional disjunction(optional &&rhs) const && {
return has_value() ? std::move(*this) : std::move(rhs);
}
#endif
/// Takes the value out of the optional, leaving it empty
optional take() {
optional ret = std::move(*this);
reset();
return ret;
}
using value_type = T &;
/// Constructs an optional that does not contain a value.
constexpr optional() noexcept : m_value(nullptr) {}
constexpr optional(nullopt_t) noexcept : m_value(nullptr) {}
/// Copy constructor
///
/// If `rhs` contains a value, the stored value is direct-initialized with
/// it. Otherwise, the constructed optional is empty.
TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) noexcept = default;
/// Move constructor
///
/// If `rhs` contains a value, the stored value is direct-initialized with
/// it. Otherwise, the constructed optional is empty.
TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default;
/// Constructs the stored value with `u`.
template <class U = T,
detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value>
* = nullptr>
constexpr optional(U &&u) noexcept : m_value(std::addressof(u)) {
static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue");
}
template <class U>
constexpr explicit optional(const optional<U> &rhs) noexcept : optional(*rhs) {}
/// No-op
~optional() = default;
/// Assignment to empty.
///
/// Destroys the current value if there is one.
optional &operator=(nullopt_t) noexcept {
m_value = nullptr;
return *this;
}
/// Copy assignment.
///
/// Rebinds this optional to the referee of `rhs` if there is one. Otherwise
/// resets the stored value in `*this`.
optional &operator=(const optional &rhs) = default;
/// Rebinds this optional to `u`.
template <class U = T,
detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value>
* = nullptr>
optional &operator=(U &&u) {
static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue");
m_value = std::addressof(u);
return *this;
}
/// Converting copy assignment operator.
///
/// Rebinds this optional to the referee of `rhs` if there is one. Otherwise
/// resets the stored value in `*this`.
template <class U> optional &operator=(const optional<U> &rhs) noexcept {
m_value = std::addressof(rhs.value());
return *this;
}
/// Rebinds this optional to `u`.
template <class U = T,
detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value>
* = nullptr>
optional &emplace(U &&u) noexcept {
return *this = std::forward<U>(u);
}
void swap(optional &rhs) noexcept { std::swap(m_value, rhs.m_value); }
/// Returns a pointer to the stored value
constexpr const T *operator->() const noexcept { return m_value; }
TL_OPTIONAL_11_CONSTEXPR T *operator->() noexcept { return m_value; }
/// Returns the stored value
TL_OPTIONAL_11_CONSTEXPR T &operator*() noexcept { return *m_value; }
constexpr const T &operator*() const noexcept { return *m_value; }
constexpr bool has_value() const noexcept { return m_value != nullptr; }
constexpr explicit operator bool() const noexcept {
return m_value != nullptr;
}
/// Returns the contained value if there is one, otherwise throws bad_optional_access
TL_OPTIONAL_11_CONSTEXPR T &value() {
if (has_value())
return *m_value;
throw bad_optional_access();
}
TL_OPTIONAL_11_CONSTEXPR const T &value() const {
if (has_value())
return *m_value;
throw bad_optional_access();
}
/// Returns the stored value if there is one, otherwise returns `u`
template <class U> constexpr T value_or(U &&u) const & noexcept {
static_assert(std::is_copy_constructible<T>::value &&
std::is_convertible<U &&, T>::value,
"T must be copy constructible and convertible from U");
return has_value() ? **this : static_cast<T>(std::forward<U>(u));
}
/// \group value_or
template <class U> TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && noexcept {
static_assert(std::is_move_constructible<T>::value &&
std::is_convertible<U &&, T>::value,
"T must be move constructible and convertible from U");
return has_value() ? **this : static_cast<T>(std::forward<U>(u));
}
/// Destroys the stored value if one exists, making the optional empty
void reset() noexcept { m_value = nullptr; }
private:
T *m_value;
}; // namespace tl
} // namespace tl
namespace std {
// TODO SFINAE
template <class T> struct hash<tl::optional<T>> {
::std::size_t operator()(const tl::optional<T> &o) const {
if (!o.has_value())
return 0;
return std::hash<tl::detail::remove_const_t<T>>()(*o);
}
};
} // namespace std
#endif
//Copyright (c) 2017 Facebook Inc. (Soumith Chintala), //Copyright (c) 2017 Facebook Inc. (Soumith Chintala),
//All rights reserved. //All rights reserved.
#include "pybind/sox/io.h" #include "paddlespeech/audio/src/pybind/sox/io.h"
PYBIND11_MODULE(_paddleaudio, m) { PYBIND11_MODULE(_paddleaudio, m) {
m.def("get_info_file", &paddleaudio::sox_io::get_info_file, m.def("get_info_file", &paddleaudio::sox_io::get_info_file,
......
//Copyright (c) 2017 Facebook Inc. (Soumith Chintala), //Copyright (c) 2017 Facebook Inc. (Soumith Chintala),
//All rights reserved. //All rights reserved.
#include "pybind/sox/io.h" #include "paddlespeech/audio/src/pybind/sox/io.h"
#include "pybind/sox/utils.h" #include "paddlespeech/audio/src/pybind/sox/utils.h"
using namespace paddleaudio::sox_utils; using namespace paddleaudio::sox_utils;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
#ifndef PADDLEAUDIO_PYBIND_SOX_IO_H #ifndef PADDLEAUDIO_PYBIND_SOX_IO_H
#define PADDLEAUDIO_PYBIND_SOX_IO_H #define PADDLEAUDIO_PYBIND_SOX_IO_H
#include "pybind/sox/utils.h" #include "paddlespeech/audio/src/pybind/sox/utils.h"
namespace paddleaudio { namespace paddleaudio {
namespace sox_io { namespace sox_io {
......
//Copyright (c) 2017 Facebook Inc. (Soumith Chintala), //Copyright (c) 2017 Facebook Inc. (Soumith Chintala),
//All rights reserved. //All rights reserved.
#include "pybind/sox/utils.h" #include "paddlespeech/audio/src/pybind/sox/utils.h"
#include <sstream> #include <sstream>
......
// #include "sox/effects.h"
// #include "sox/effects_chain.h"
#include "sox/io.h"
#include "sox/types.h"
#include "sox/utils.h"
using namespace torch::indexing;
using namespace paddleaudio::sox_utils;
namespace paddleaudio {
namespace sox_io {
tl::optional<MetaDataTuple> get_info_file(
const std::string& path,
const tl::optional<std::string>& format) {
SoxFormat sf(sox_open_read(
path.c_str(),
/*signal=*/nullptr,
/*encoding=*/nullptr,
/*filetype=*/format.has_value() ? format.value().c_str() : nullptr));
if (static_cast<sox_format_t*>(sf) == nullptr ||
sf->encoding.encoding == SOX_ENCODING_UNKNOWN) {
return {};
}
return std::forward_as_tuple(
static_cast<int64_t>(sf->signal.rate),
static_cast<int64_t>(sf->signal.length / sf->signal.channels),
static_cast<int64_t>(sf->signal.channels),
static_cast<int64_t>(sf->encoding.bits_per_sample),
get_encoding(sf->encoding.encoding));
}
std::vector<std::vector<std::string>> get_effects(
const tl::optional<int64_t>& frame_offset,
const tl::optional<int64_t>& num_frames) {
const auto offset = frame_offset.value_or(0);
if (offset < 0) {
throw std::runtime_error(
"Invalid argument: frame_offset must be non-negative.");
}
const auto frames = num_frames.value_or(-1);
if (frames == 0 || frames < -1) {
throw std::runtime_error(
"Invalid argument: num_frames must be -1 or greater than 0.");
}
std::vector<std::vector<std::string>> effects;
if (frames != -1) {
std::ostringstream os_offset, os_frames;
os_offset << offset << "s";
os_frames << "+" << frames << "s";
effects.emplace_back(
std::vector<std::string>{"trim", os_offset.str(), os_frames.str()});
} else if (offset != 0) {
std::ostringstream os_offset;
os_offset << offset << "s";
effects.emplace_back(std::vector<std::string>{"trim", os_offset.str()});
}
return effects;
}
tl::optional<std::tuple<torch::Tensor, int64_t>> load_audio_file(
const std::string& path,
const tl::optional<int64_t>& frame_offset,
const tl::optional<int64_t>& num_frames,
tl::optional<bool> normalize,
tl::optional<bool> channels_first,
const tl::optional<std::string>& format) {
auto effects = get_effects(frame_offset, num_frames);
return paddleaudio::sox_effects::apply_effects_file(
path, effects, normalize, channels_first, format);
}
void save_audio_file(
const std::string& path,
torch::Tensor tensor,
int64_t sample_rate,
bool channels_first,
tl::optional<double> compression,
tl::optional<std::string> format,
tl::optional<std::string> encoding,
tl::optional<int64_t> bits_per_sample) {
validate_input_tensor(tensor);
const auto filetype = [&]() {
if (format.has_value())
return format.value();
return get_filetype(path);
}();
if (filetype == "amr-nb") {
const auto num_channels = tensor.size(channels_first ? 0 : 1);
TORCH_CHECK(
num_channels == 1, "amr-nb format only supports single channel audio.");
} else if (filetype == "htk") {
const auto num_channels = tensor.size(channels_first ? 0 : 1);
TORCH_CHECK(
num_channels == 1, "htk format only supports single channel audio.");
} else if (filetype == "gsm") {
const auto num_channels = tensor.size(channels_first ? 0 : 1);
TORCH_CHECK(
num_channels == 1, "gsm format only supports single channel audio.");
TORCH_CHECK(
sample_rate == 8000,
"gsm format only supports a sampling rate of 8kHz.");
}
const auto signal_info =
get_signalinfo(&tensor, sample_rate, filetype, channels_first);
const auto encoding_info = get_encodinginfo_for_save(
filetype, tensor.dtype(), compression, encoding, bits_per_sample);
SoxFormat sf(sox_open_write(
path.c_str(),
&signal_info,
&encoding_info,
/*filetype=*/filetype.c_str(),
/*oob=*/nullptr,
/*overwrite_permitted=*/nullptr));
if (static_cast<sox_format_t*>(sf) == nullptr) {
throw std::runtime_error(
"Error saving audio file: failed to open file " + path);
}
paddleaudio::sox_effects_chain::SoxEffectsChain chain(
/*input_encoding=*/get_tensor_encodinginfo(tensor.dtype()),
/*output_encoding=*/sf->encoding);
chain.addInputTensor(&tensor, sample_rate, channels_first);
chain.addOutputFile(sf);
chain.run();
}
TORCH_LIBRARY_FRAGMENT(paddleaudio, m) {
m.def("paddleaudio::sox_io_get_info", &paddleaudio::sox_io::get_info_file);
m.def(
"paddleaudio::sox_io_load_audio_file",
&paddleaudio::sox_io::load_audio_file);
m.def(
"paddleaudio::sox_io_save_audio_file",
&paddleaudio::sox_io::save_audio_file);
}
} // namespace sox_io
} // namespace paddleaudio
\ No newline at end of file
//Copyright (c) 2017 Facebook Inc. (Soumith Chintala),
//All rights reserved.
#ifndef PADDLEAUDIO_SOX_IO_H
#define PADDLEAUDIO_SOX_IO_H
// #include "sox/utils.h"
#include "optional/optional.hpp"
namespace paddleaudio {
namespace sox_io {
auto get_effects(
const tl::optional<int64_t>& frame_offset,
const tl::optional<int64_t>& num_frames)
-> std::vector<std::vector<std::string>>;
using MetaDataTuple =
std::tuple<int64_t, int64_t, int64_t, int64_t, std::string>;
tl::optional<MetaDataTuple> get_info_file(
const std::string& path,
const tl::optional<std::string>& format);
tl::optional<std::tuple<torch::Tensor, int64_t>> load_audio_file(
const std::string& path,
const tl::optional<int64_t>& frame_offset,
const tl::optional<int64_t>& num_frames,
tl::optional<bool> normalize,
tl::optional<bool> channels_first,
const tl::optional<std::string>& format);
void save_audio_file(
const std::string& path,
torch::Tensor tensor,
int64_t sample_rate,
bool channels_first,
tl::optional<double> compression,
tl::optional<std::string> format,
tl::optional<std::string> encoding,
tl::optional<int64_t> bits_per_sample);
} // namespace sox_io
} // namespace paddleaudio
#endif
\ No newline at end of file
namespace paddleaudio {
namespace {
bool is_sox_available() {
#ifdef INCLUDE_SOX
return true;
#else
return false;
#endif
}
bool is_kaldi_available() {
#ifdef INCLUDE_KALDI
return true;
#else
return false;
#endif
}
// It tells whether paddleaudio was compiled with ffmpeg
// not the runtime availability.
bool is_ffmpeg_available() {
#ifdef USE_FFMPEG
return true;
#else
return false;
#endif
}
} // namespace
} // namespace paddleaudio
\ No newline at end of file
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
add_library(libsox INTERFACE)
if (BUILD_SOX) if (BUILD_SOX)
add_subdirectory(sox) add_subdirectory(sox)
target_include_directories(libsox INTERFACE ${SOX_INCLUDE_DIR})
target_link_libraries(libsox INTERFACE ${SOX_LIBRARIES})
endif() endif()
include_directories( include_directories(
......
...@@ -17,22 +17,24 @@ set(envs ...@@ -17,22 +17,24 @@ set(envs
"CFLAGS=-I${INSTALL_DIR}/include -fvisibility=hidden $ENV{CFLAGS}" "CFLAGS=-I${INSTALL_DIR}/include -fvisibility=hidden $ENV{CFLAGS}"
) )
ExternalProject_Add(mad if (BUILD_MAD)
PREFIX ${CMAKE_CURRENT_BINARY_DIR} ExternalProject_Add(mad
DOWNLOAD_DIR ${ARCHIVE_DIR} PREFIX ${CMAKE_CURRENT_BINARY_DIR}
URL https://downloads.sourceforge.net/project/mad/libmad/0.15.1b/libmad-0.15.1b.tar.gz DOWNLOAD_DIR ${ARCHIVE_DIR}
URL_HASH SHA256=bbfac3ed6bfbc2823d3775ebb931087371e142bb0e9bb1bee51a76a6e0078690 URL https://downloads.sourceforge.net/project/mad/libmad/0.15.1b/libmad-0.15.1b.tar.gz
PATCH_COMMAND patch < ${patch_dir}/libmad.patch && cp ${patch_dir}/config.guess ${patch_dir}/config.sub ${CMAKE_CURRENT_BINARY_DIR}/src/mad/ URL_HASH SHA256=bbfac3ed6bfbc2823d3775ebb931087371e142bb0e9bb1bee51a76a6e0078690
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${envs} ${CMAKE_CURRENT_BINARY_DIR}/src/mad/configure ${COMMON_ARGS} PATCH_COMMAND patch < ${patch_dir}/libmad.patch && cp ${patch_dir}/config.guess ${patch_dir}/config.sub ${CMAKE_CURRENT_BINARY_DIR}/src/mad/
DOWNLOAD_NO_PROGRESS ON CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${envs} ${CMAKE_CURRENT_BINARY_DIR}/src/mad/configure ${COMMON_ARGS}
LOG_DOWNLOAD ON DOWNLOAD_NO_PROGRESS ON
LOG_UPDATE ON LOG_DOWNLOAD ON
LOG_CONFIGURE ON LOG_UPDATE ON
LOG_BUILD ON LOG_CONFIGURE ON
LOG_INSTALL ON LOG_BUILD ON
LOG_MERGED_STDOUTERR ON LOG_INSTALL ON
LOG_OUTPUT_ON_FAILURE ON LOG_MERGED_STDOUTERR ON
) LOG_OUTPUT_ON_FAILURE ON
)
endif (BUILD_MAD)
ExternalProject_Add(amr ExternalProject_Add(amr
PREFIX ${CMAKE_CURRENT_BINARY_DIR} PREFIX ${CMAKE_CURRENT_BINARY_DIR}
...@@ -157,13 +159,15 @@ ExternalProject_Add(opusfile ...@@ -157,13 +159,15 @@ ExternalProject_Add(opusfile
LOG_OUTPUT_ON_FAILURE ON LOG_OUTPUT_ON_FAILURE ON
) )
# OpenMP is by default compiled against GNU OpenMP, which conflicts with the version of OpenMP that PyTorch uses.
# See https://github.com/pytorch/audio/pull/1026
# TODO: Add flags like https://github.com/suphoff/pytorch_parallel_extension_cpp/blob/master/setup.py
set(SOX_OPTIONS set(SOX_OPTIONS
--disable-openmp --disable-openmp
--with-amrnb --with-amrnb
--with-amrwb --with-amrwb
--with-flac --with-flac
--with-lame --with-lame
--with-mad
--with-oggvorbis --with-oggvorbis
--with-opus --with-opus
--without-alsa --without-alsa
...@@ -187,7 +191,6 @@ set(SOX_LIBRARIES ...@@ -187,7 +191,6 @@ set(SOX_LIBRARIES
${INSTALL_DIR}/lib/libsox.a ${INSTALL_DIR}/lib/libsox.a
${INSTALL_DIR}/lib/libopencore-amrnb.a ${INSTALL_DIR}/lib/libopencore-amrnb.a
${INSTALL_DIR}/lib/libopencore-amrwb.a ${INSTALL_DIR}/lib/libopencore-amrwb.a
${INSTALL_DIR}/lib/libmad.a
${INSTALL_DIR}/lib/libmp3lame.a ${INSTALL_DIR}/lib/libmp3lame.a
${INSTALL_DIR}/lib/libFLAC.a ${INSTALL_DIR}/lib/libFLAC.a
${INSTALL_DIR}/lib/libopusfile.a ${INSTALL_DIR}/lib/libopusfile.a
...@@ -198,9 +201,37 @@ set(SOX_LIBRARIES ...@@ -198,9 +201,37 @@ set(SOX_LIBRARIES
${INSTALL_DIR}/lib/libogg.a ${INSTALL_DIR}/lib/libogg.a
) )
set(sox_depends
ogg flac vorbis opusfile lame amr
)
if (BUILD_MAD)
list(
APPEND
SOX_OPTIONS
--with-mad
)
list(
APPEND
SOX_LIBRARIES
${INSTALL_DIR}/lib/libmad.a
)
list(
APPEND
sox_depends
mad
)
else ()
list(
APPEND
SOX_OPTIONS
--without-mad
)
endif (BUILD_MAD)
ExternalProject_Add(sox ExternalProject_Add(sox
PREFIX ${CMAKE_CURRENT_BINARY_DIR} PREFIX ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ogg flac vorbis opusfile lame mad amr DEPENDS ${sox_depends}
DOWNLOAD_DIR ${ARCHIVE_DIR} DOWNLOAD_DIR ${ARCHIVE_DIR}
URL https://downloads.sourceforge.net/project/sox/sox/14.4.2/sox-14.4.2.tar.bz2 URL https://downloads.sourceforge.net/project/sox/sox/14.4.2/sox-14.4.2.tar.bz2
URL_HASH SHA256=81a6956d4330e75b5827316e44ae381e6f1e8928003c6aa45896da9041ea149c URL_HASH SHA256=81a6956d4330e75b5827316e44ae381e6f1e8928003c6aa45896da9041ea149c
...@@ -217,6 +248,7 @@ ExternalProject_Add(sox ...@@ -217,6 +248,7 @@ ExternalProject_Add(sox
LOG_OUTPUT_ON_FAILURE ON LOG_OUTPUT_ON_FAILURE ON
) )
add_library(libsox INTERFACE)
add_dependencies(libsox sox) add_dependencies(libsox sox)
set(SOX_INCLUDE_DIR ${INSTALL_DIR}/include PARENT_SCOPE) target_include_directories(libsox INTERFACE ${INSTALL_DIR}/include)
set(SOX_LIBRARIES ${SOX_LIBRARIES} PARENT_SCOPE) target_link_libraries(libsox INTERFACE ${SOX_LIBRARIES})
\ No newline at end of file
...@@ -18,7 +18,11 @@ import os ...@@ -18,7 +18,11 @@ import os
import subprocess as sp import subprocess as sp
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Union
from typing import Tuple
from typing import List
import distutils.command.clean
from setuptools import Command from setuptools import Command
from setuptools import find_packages from setuptools import find_packages
from setuptools import setup from setuptools import setup
...@@ -26,7 +30,9 @@ from setuptools.command.develop import develop ...@@ -26,7 +30,9 @@ from setuptools.command.develop import develop
from setuptools.command.install import install from setuptools.command.install import install
from setuptools.command.test import test from setuptools.command.test import test
HERE = Path(os.path.abspath(os.path.dirname(__file__))) from tools import setup_helpers
HERE = Path(__file__).parent.resolve()
VERSION = '0.0.0' VERSION = '0.0.0'
COMMITID = 'none' COMMITID = 'none'
...@@ -114,9 +120,15 @@ def check_call(cmd: str, shell=False, executable=None): ...@@ -114,9 +120,15 @@ def check_call(cmd: str, shell=False, executable=None):
raise e raise e
def check_output(cmd: str, shell=False): def check_output(cmd: Union[str, List[str], Tuple[str]], shell=False):
try: try:
out_bytes = sp.check_output(cmd.split())
if isinstance(cmd, (list, tuple)):
cmds = cmd
else:
cmds = cmd.split()
out_bytes = sp.check_output(cmds)
except sp.CalledProcessError as e: except sp.CalledProcessError as e:
out_bytes = e.output # Output generated before error out_bytes = e.output # Output generated before error
code = e.returncode # Return code code = e.returncode # Return code
...@@ -216,121 +228,135 @@ class UploadCommand(Command): ...@@ -216,121 +228,135 @@ class UploadCommand(Command):
################################# Version ################################## ################################# Version ##################################
def write_version_py(filename='paddlespeech/__init__.py'): def _get_version(sha):
import paddlespeech version = VERSION
if hasattr(paddlespeech, if os.getenv("BUILD_VERSION"):
"__version__") and paddlespeech.__version__ == VERSION: version = os.getenv("BUILD_VERSION")
return elif sha is not None:
with open(filename, "a") as f: version += "+" + sha[:7]
out_str = f"\n__version__ = '{VERSION}'\n" return version
print(out_str)
f.write(f"\n__version__ = '{VERSION}'\n")
COMMITID = check_output("git rev-parse HEAD")
with open(filename, 'a') as f:
out_str = f"\n__commit__ = '{COMMITID}'\n"
print(out_str)
f.write(f"\n__commit__ = '{COMMITID}'\n")
print(f"{inspect.currentframe().f_code.co_name} done")
def remove_version_py(filename='paddlespeech/__init__.py'):
with open(filename, "r") as f:
lines = f.readlines()
with open(filename, "w") as f:
for line in lines:
if "__version__" in line or "__commit__" in line:
continue
f.write(line)
print(f"{inspect.currentframe().f_code.co_name} done")
@contextlib.contextmanager def _make_version_file(version, sha):
def version_info(): sha = "Unknown" if sha is None else sha
write_version_py() version_path = HERE / "paddlespeech" / "version.py"
yield with open(version_path, "w") as f:
remove_version_py() f.write(f"__version__ = '{version}'\n")
f.write(f"__commit__ = '{sha}'\n")
################################# Steup ################################## ################################# Steup ##################################
setup_info = dict( class clean(distutils.command.clean.clean):
# Metadata def run(self):
name='paddlespeech', # Run default behavior first
version=VERSION, distutils.command.clean.clean.run(self)
author='PaddlePaddle Speech and Language Team',
author_email='paddlesl@baidu.com', # Remove torchaudio extension
url='https://github.com/PaddlePaddle/PaddleSpeech', for path in (ROOT_DIR / "paddlespeech").glob("**/*.so"):
license='Apache 2.0', print(f"removing '{path}'")
description='Speech tools and models based on Paddlepaddle', path.unlink()
long_description=read("README.md"), # Remove build directory
long_description_content_type="text/markdown", build_dirs = [
keywords=[ ROOT_DIR / "build",
"speech",
"asr",
"tts",
"streaming asr"
"streaming tts"
"audio process"
"speaker verfication",
"speech classfication",
"text frontend",
"MFA",
"paddlepaddle",
"beam search",
"ctcdecoder",
"deepspeech2",
"transformer",
"conformer",
"fastspeech",
"vocoder",
"pwgan",
"melgan",
"mb-melgan",
"hifigan",
"gan",
"wfst decoder",
],
python_requires='>=3.7',
install_requires=requirements["install"],
extras_require={
'develop':
requirements["develop"],
'doc': [
"sphinx", "sphinx-rtd-theme", "numpydoc", "myst_parser",
"recommonmark>=0.5.0", "sphinx-markdown-tables", "sphinx-autobuild"
],
'test': ['nose', 'torchaudio==0.10.2'],
},
cmdclass={
'develop': DevelopCommand,
'install': InstallCommand,
'upload': UploadCommand,
'test': TestCommand,
},
# Package info
packages=find_packages(include=('paddlespeech*')),
zip_safe=True,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Intended Audience :: Science/Research',
'Topic :: Scientific/Engineering :: Artificial Intelligence',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
],
entry_points={
'console_scripts': [
'paddlespeech=paddlespeech.cli.entry:_execute',
'paddlespeech_server=paddlespeech.server.entry:server_execute',
'paddlespeech_client=paddlespeech.server.entry:client_execute'
] ]
}) for path in build_dirs:
if path.exists():
print(f"removing '{path}' (and everything under it)")
shutil.rmtree(str(path), ignore_errors=True)
def main():
sha = check_output(["git", "rev-parse", "HEAD"]) # commit id
branch = check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
tag = check_output(["git", "describe", "--tags", "--exact-match", "@"])
print("-- Git branch:", branch)
print("-- Git SHA:", sha)
print("-- Git tag:", tag)
version = _get_version(sha)
print("-- Building version", version)
_make_version_file(version, sha)
setup_info = dict(
# Metadata
name='paddlespeech',
version=VERSION,
author='PaddlePaddle Speech and Language Team',
author_email='paddlesl@baidu.com',
url='https://github.com/PaddlePaddle/PaddleSpeech',
license='Apache 2.0',
description='Speech tools and models based on Paddlepaddle',
long_description=read("README.md"),
long_description_content_type="text/markdown",
keywords=[
"speech",
"asr",
"tts",
"streaming asr"
"streaming tts"
"audio process"
"speaker verfication",
"speech classfication",
"text frontend",
"MFA",
"paddlepaddle",
"beam search",
"ctcdecoder",
"deepspeech2",
"transformer",
"conformer",
"fastspeech",
"vocoder",
"pwgan",
"melgan",
"mb-melgan",
"hifigan",
"gan",
"wfst decoder",
],
python_requires='>=3.7',
install_requires=requirements["install"],
extras_require={
'develop':
requirements["develop"],
'doc': [
"sphinx", "sphinx-rtd-theme", "numpydoc", "myst_parser",
"recommonmark>=0.5.0", "sphinx-markdown-tables", "sphinx-autobuild"
],
'test': ['nose', 'torchaudio==0.10.2'],
},
cmdclass={
'develop': DevelopCommand,
'install': InstallCommand,
'upload': UploadCommand,
'test': TestCommand,
"build_ext": setup_helpers.CMakeBuild,
"clean": clean,
},
# Package info
packages=find_packages(include=('paddlespeech*')),
ext_modules=setup_helpers.get_ext_modules(),
zip_safe=True,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Intended Audience :: Science/Research',
'Topic :: Scientific/Engineering :: Artificial Intelligence',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
],
entry_points={
'console_scripts': [
'paddlespeech=paddlespeech.cli.entry:_execute',
'paddlespeech_server=paddlespeech.server.entry:server_execute',
'paddlespeech_client=paddlespeech.server.entry:client_execute'
]
})
with version_info():
setup(**setup_info) setup(**setup_info)
if __name__ == '__main__':
main()
\ No newline at end of file
from .extension import *
\ No newline at end of file
import distutils.sysconfig
import os
import platform
import subprocess
from pathlib import Path
from setuptools import Extension
from setuptools.command.build_ext import build_ext
__all__ = [
"get_ext_modules",
"CMakeBuild",
]
_THIS_DIR = Path(__file__).parent.resolve()
_ROOT_DIR = _THIS_DIR.parent.parent.resolve()
_PADDLESPEECH_DIR = _ROOT_DIR / "paddlespeech"
def _get_build(var, default=False):
if var not in os.environ:
return default
val = os.environ.get(var, "0")
trues = ["1", "true", "TRUE", "on", "ON", "yes", "YES"]
falses = ["0", "false", "FALSE", "off", "OFF", "no", "NO"]
if val in trues:
return True
if val not in falses:
print(f"WARNING: Unexpected environment variable value `{var}={val}`. " f"Expected one of {trues + falses}")
return False
_BUILD_SOX = False if platform.system() == "Windows" else _get_build("BUILD_SOX", True)
_BUILD_MAD = _get_build("BUILD_MAD", False)
# _BUILD_KALDI = False if platform.system() == "Windows" else _get_build("BUILD_KALDI", True)
# _BUILD_RNNT = _get_build("BUILD_RNNT", True)
# _BUILD_CTC_DECODER = False if platform.system() == "Windows" else _get_build("BUILD_CTC_DECODER", True)
# _USE_FFMPEG = _get_build("USE_FFMPEG", False)
# _USE_ROCM = _get_build("USE_ROCM", torch.cuda.is_available() and torch.version.hip is not None)
# _USE_CUDA = _get_build("USE_CUDA", torch.cuda.is_available() and torch.version.hip is None)
# _USE_OPENMP = _get_build("USE_OPENMP", True) and "ATen parallel backend: OpenMP" in torch.__config__.parallel_info()
_PADDLESPEECH_CUDA_ARCH_LIST = os.environ.get("PADDLESPEECH_CUDA_ARCH_LIST", None)
def get_ext_modules():
modules = [
Extension(name="paddlespeech.audio.lib.libpaddleaudio", sources=[]),
Extension(name="paddlespeech.audio._paddleaudio", sources=[]),
]
return modules
# Based off of
# https://github.com/pybind/cmake_example/blob/580c5fd29d4651db99d8874714b07c0c49a53f8a/setup.py
class CMakeBuild(build_ext):
def run(self):
try:
subprocess.check_output(["cmake", "--version"])
except OSError:
raise RuntimeError("CMake is not available.") from None
super().run()
def build_extension(self, ext):
# Since two library files (libpaddleaudio and _paddleaudio) need to be
# recognized by setuptools, we instantiate `Extension` twice. (see `get_ext_modules`)
# This leads to the situation where this `build_extension` method is called twice.
# However, the following `cmake` command will build all of them at the same time,
# so, we do not need to perform `cmake` twice.
# Therefore we call `cmake` only for `paddleaudio._paddleaudio`.
if ext.name != "paddlespeech.audio._paddleaudio":
return
extdir = os.path.abspath(os.path.dirname(self.get_ext_filename(ext.name)))
# required for auto-detection of auxiliary "native" libs
if not extdir.endswith(os.path.sep):
extdir += os.path.sep
cfg = "Debug" if self.debug else "Release"
cmake_args = [
f"-DCMAKE_BUILD_TYPE={cfg}",
# f"-DCMAKE_PREFIX_PATH={torch.utils.cmake_prefix_path}",
f"-DCMAKE_INSTALL_PREFIX={extdir}",
"-DCMAKE_VERBOSE_MAKEFILE=ON",
f"-DPython_INCLUDE_DIR={distutils.sysconfig.get_python_inc()}",
f"-DBUILD_SOX:BOOL={'ON' if _BUILD_SOX else 'OFF'}",
f"-DBUILD_MAD:BOOL={'ON' if _BUILD_MAD else 'OFF'}",
# f"-DBUILD_KALDI:BOOL={'ON' if _BUILD_KALDI else 'OFF'}",
# f"-DBUILD_RNNT:BOOL={'ON' if _BUILD_RNNT else 'OFF'}",
# f"-DBUILD_CTC_DECODER:BOOL={'ON' if _BUILD_CTC_DECODER else 'OFF'}",
"-DBUILD_PADDLEAUDIO_PYTHON_EXTENSION:BOOL=ON",
# f"-DUSE_ROCM:BOOL={'ON' if _USE_ROCM else 'OFF'}",
# f"-DUSE_CUDA:BOOL={'ON' if _USE_CUDA else 'OFF'}",
# f"-DUSE_OPENMP:BOOL={'ON' if _USE_OPENMP else 'OFF'}",
# f"-DUSE_FFMPEG:BOOL={'ON' if _USE_FFMPEG else 'OFF'}",
]
build_args = ["--target", "install"]
# Pass CUDA architecture to cmake
if _PADDLESPEECH_CUDA_ARCH_LIST is not None:
# Convert MAJOR.MINOR[+PTX] list to new style one
# defined at https://cmake.org/cmake/help/latest/prop_tgt/CUDA_ARCHITECTURES.html
_arches = _PADDLESPEECH_CUDA_ARCH_LIST.replace(".", "").replace(" ", ";").split(";")
_arches = [arch[:-4] if arch.endswith("+PTX") else f"{arch}-real" for arch in _arches]
cmake_args += [f"-DCMAKE_CUDA_ARCHITECTURES={';'.join(_arches)}"]
# Default to Ninja
if "CMAKE_GENERATOR" not in os.environ or platform.system() == "Windows":
cmake_args += ["-GNinja"]
if platform.system() == "Windows":
import sys
python_version = sys.version_info
cmake_args += [
"-DCMAKE_C_COMPILER=cl",
"-DCMAKE_CXX_COMPILER=cl",
f"-DPYTHON_VERSION={python_version.major}.{python_version.minor}",
]
# Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level
# across all generators.
if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ:
# self.parallel is a Python 3 only way to set parallel jobs by hand
# using -j in the build_ext call, not supported by pip or PyPA-build.
if hasattr(self, "parallel") and self.parallel:
# CMake 3.12+ only.
build_args += ["-j{}".format(self.parallel)]
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
print(f"cmake {_ROOT_DIR} {' '.join(cmake_args)}, cwd={self.build_temp}")
subprocess.check_call(["cmake", str(_ROOT_DIR)] + cmake_args, cwd=self.build_temp)
print(f"cmake --build . {' '.join(build_args)}, cwd={self.build_temp}")
subprocess.check_call(["cmake", "--build", "."] + build_args, cwd=self.build_temp)
def get_ext_filename(self, fullname):
ext_filename = super().get_ext_filename(fullname)
ext_filename_parts = ext_filename.split(".")
without_abi = ext_filename_parts[:-2] + ext_filename_parts[-1:]
ext_filename = ".".join(without_abi)
return ext_filename
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册