Giter Site home page Giter Site logo

span's Introduction

Standard License Build Status Build status Try it on godbolt online

std::span implementation for C++11 and later

This repository contains a single-header implementation of C++20's std::span, conforming to the C++20 committee draft. It is compatible with C++11, but will use newer language features if they are available.

It differs from the implementation in the Microsoft GSL in that it is single-header and does not depend on any other GSL facilities. It also works with C++11, while the GSL version requires C++14.

Usage

The recommended way to use the implementation simply copy the file span.hpp from include/tcb/ into your own sources and #include it like any other header. By default, it lives in namespace tcb, but this can be customised by setting the macro TCB_SPAN_NAMESPACE_NAME to an appropriate string before #include-ing the header -- or simply edit the source code.

The rest of the repository contains testing machinery, and is not required for use.

Compatibility

This implementation requires a conforming C++11 (or later) compiler, and is tested as far back as GCC 5, Clang 3.5 and MSVC 2015 Update 3. Older compilers may work, but this is not guaranteed.

Documentation

Documentation for std::span is available on cppreference.

Implementation Notes

Bounds Checking

This implementation of span includes optional bounds checking, which is handled either by throwing an exception or by calling std::terminate().

The default behaviour with C++14 and later is to check the macro NDEBUG: if this is set, bounds checking is disabled. Otherwise, std::terminate() will be called if there is a precondition violation (i.e. the same behaviour as assert()). If you wish to terminate on errors even if NDEBUG is set, define the symbol TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION before #include-ing the header.

Alternatively, if you want to throw on a contract violation, define TCB_SPAN_THROW_ON_CONTRACT_VIOLATION. This will throw an exception of an implementation-defined type (deriving from std::logic_error), allowing cleanup to happen. Note that defining this symbol will cause the checks to be run even if NDEBUG is set.

Lastly, if you wish to disable contract checking even in debug builds, #define TCB_SPAN_NO_CONTRACT_CHECKING.

Under C++11, due to the restrictions on constexpr functions, contract checking is disabled by default even if NDEBUG is not set. You can change this by defining either of the above symbols, but this will result in most of span's interface becoming non-constexpr.

constexpr

This implementation is fully constexpr under C++17 and later. Under earlier versions, it is "as constexpr as possible".

Note that even in C++17, it is generally not possible to declare a span as non-default constructed constexpr variable, for the same reason that you cannot form a constexpr pointer to a value: it involves taking the address of a compile-time variable in a way that would be visible at run-time. You can however use a span freely in a constexpr function. For example:

// Okay, even in C++11
constexpr std::ptrdiff_t get_span_size(span<const int> span)
{
    return span.size();
}

constexpr int arr[] = {1, 2, 3};
constexpr auto size = get_span_size(arr); // Okay
constexpr span<const int> span{arr}; // ERROR -- not a constant expression
constexpr const int* p = arr; // ERROR -- same

Constructor deduction guides are provided if the compiler supports them. For older compilers, a set of make_span() functions are provided as an extension which use the same logic, for example:

constexpr int c_array[] = {1, 2, 3};
std::array<int, 3> std_array{1, 2, 3};
const std::vector<int> vec{1, 2, 3};

auto s1 = make_span(c_array);   // returns span<const int, 3>
auto s2 = make_span(std_array); // returns span<int, 3>
auto s3 = make_span(vec);       // returns span<const int, dynamic_extent>

Alternatives

  • Microsoft/GSL: The original span reference implementation from which std::span was born.

  • martinmoene/span_lite: An alternative implementation which offers C++98 compatibility.

span's People

Contributors

brandl-muc avatar centurn avatar kimci86 avatar tcbrindle avatar tybl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

span's Issues

Linking problems with multiple translation units

The internal function contract_violation() shares the same function signature between two different versions, depending on whether TCB_SPAN_THROW_ON_CONTRACT_VIOLATION is defined.

This is no problem until multiple translation units are linked together and TCB_SPAN_THROW_ON_CONTRACT_VIOLATION is defined in some but not all of them. The linker chooses one of the definitions and throws away the other, which means that the translation units that turned on TCB_SPAN_THROW_ON_CONTRACT_VIOLATION can end up erroneously calling the std::terminate version if that is the one the linker chose. Or vice-versa I presume. I see this in debug builds using GCC 8 or 10.

You can fix this problem by giving the throwing version a different function signature. For example:
inline void contract_violation(const char* msg, int = 0)

It also seems to me that the throwing version should be [[noreturn]] like the std::terminate version, since it too never returns and [[noreturn]] because of throw was explicitly contemplated in many [[noreturn]] examples I've seen.

Latest C++20 draft has no cbegin/cend/crbegin/crend (anymore)

Hi, Tristian.

The Committee has expelled the const versions of iterators out of std::span.
If you're keeping your tcb::span to be C++20 compliant would not it be reasonable to
remove those const iterators out of tcb::span as well?

I mean "commenting out" (or wraping up in macro conditions) the following code:

    constexpr const_iterator cbegin() const noexcept { return begin(); }
    constexpr const_iterator cend() const noexcept { return end(); }
    const_reverse_iterator crbegin() const noexcept ...
    const_reverse_iterator crend() const noexcept ...

Сonfirmative links:
LWG-3320
LLVM on std::span

‘terminate’ is not a member of ‘std’

When compiling with exceptions disabled, I run into the following issue on gcc 12.1.0 using c++17:

error: ‘terminate’ is not a member of ‘std’
   73 |     std::terminate();
      |          ^~~~~~~~~

I need to include <exception> before span.hpp in order to fix it. Am I doing something wrong?

span type missing const_iterator member type

Great implementation, thanks! I'm trying to use span with gtest's ElementsAreArray. Unfortunately since span is missing a const_iterator member type, it won't compile. It looks like the const iterator begin/end are also missing. Admittedly, I haven't given this a lot of thought. Looking at https://en.cppreference.com/w/cpp/container/span I see that the types are missing there also. Perhaps there is a well thought-out reason?

Is there a reasonable solution?

Can tcb::span be forward-declared safely?

I have several using (typedef) declarations for various tcb::span-based types, that I would like to move to a mylib/fwd.h header, which is not supposed to include much, if at all. Thus I'd like to forward-declare tcb::span itself there too, instead of including the header. Is it possible? Beside the main struct, I don't think I need anything else, but just in case, what else could be fwd-decl there too?

Compilation error when trying to emplace spans in a vector (while STL span works fine)

Hi!
First of all thanks for this work.
While experimenting with this span implementation, I have found a case where this span implementation throws compilation errors while the standard span implementation works fine. At the end of this comment I attach a link to a code snipet reproducing this behaviour.

The particular usage is creating a vector of spans by using subspan method of a span covering the whole memory section.
I had also compilation errors when trying to mix this span implementation with std::vector in other ways.
It would be great if it could be fixed, since it is very useful.

I have prepared a code snipet reproducing this error:
https://coliru.stacked-crooked.com/a/dafe684fef821f75
By commenting/uncommenting #define USE_STD_SPAN one can use either this implementation (copy/pasted from this repository with the namespace changed to WRE::Utils), or use the STL C++20 implementation of span.

Thank you very much in advance.

TCB_SPAN_THROW_ON_CONTRACT_VIOLATION makes this implementation non-conforming according to C++20

Hi,

TCB_SPAN_THROW_ON_CONTRACT_VIOLATION seems a nice extension to the language-defined std::span. However, every feature ends up being misused, and this is just another one.

I've come across a code base that relies on this extension for normal functioning. While reading an input stream in chunks of 64K, which are handled as spans, the code relies on tcb::span throwing an exception to request the next chunk from the stream. This should normally be done either manually writing code that checks the bounds, or by using another type that does that for you, but for some reason, the main programmer there thought that using this tcb::span extension was a good idea.

Now it's not a problem, but if some day the code is ported to C++20, and starts using std::span, the exception will disappear, and instead undefined behavior will be triggered. That programmer may not be in charge of the code, so it may happen to a programmer that knows nothing about that little detail of the tcb::span, which says it's a conforming implementation of C++20 and thus should be replaceable by std::span.

I'd either replace the statement that this is conforming to C++20 by something like "this is mostly conforming to C++20, except for ...", or just make it conforming (which would already break code such as the one I'm mentioning, but since it was invoking UB, it better gets fixed now).

Thanks,

Alex

Allow usage as submodule

Would be great if this was usable as a git-submodule or via CMakes ExternalProject or FetchContent.

This requires

  • a proper target name, e.g. tcspan
  • a namespaced alias for that target, e.g. tc::span
  • being able to exclude tests

For the latter I'd suggest to remove the enable_testing and use:

if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
  include(CTest)
endif()
if(BUILD_TESTING)
  add_subdirectory(test)
endif()

This include adds an option BUILD_TESTING with default ON and enable_testing call if the current project is not the super project (aka cmake was called for another folder and this folder was added via add_subdirectory)

PS: CMAKE_CURRENT_SOURCE_DIR as used is not required and can be omitted.

Update constructors

Since C++20 has been finalized, cppreference has updated their section on constructors. Looks like (2) and (3) now use generic iterators instead of concrete pointers and the container constructors have been replaced by the range constructor in (7).

Cannot implicitly convert between a subclass of std::vector<T*> and tcb::span<const T* const>

I have an AutoVector<T> template class in our codebase which inherits publicly from std::vector<T*> and handles ownership (created before the days of C++11).

I've noticed that I get compiler errors when I try to have an instance of this type implicitly convert to the associated span type, for example, something like

namespace Simba { namespace Support { class SqlTypeMetadata; } }

void UseMeta(simba_thirdparty_tcb::span<const Foo* const> meta);

void bar()
{
    AutoVector<Foo> meta(GetMeta());
    UseMeta(meta); // error C2440: 'return': cannot convert from 'Simba::Support::AutoVector<Simba::Support::SqlTypeMetadata>' to 'simba_thirdparty_tcb::span<const Simba::Support::SqlTypeMetadata *const ,18446744073709551615>'
    // UseMeta({ meta.data(), meta.size() }); // This works
}

similar code works fine when using std::vector<T*> directly. Looking at the C++20 std::span documentation, this should work as long as the AutoVector<T> type models the Range concept, and I think if std::vector does, so should a subclass (that doesn't explicitly delete any member functions)?

I'm having the problem in Visual Studio 2019, haven't tried any other compilers/platforms yet.

Release

Could you please release a version?

I'm going to package this project for Conan, and I'd prefer if a version/tag is available.

Warning that should be removed

TCB_SPAN_INLINE_VAR constexpr std::size_t dynamic_extent = -1;

should be:

TCB_SPAN_INLINE_VAR constexpr std::size_t dynamic_extent = ~std::size_t(0);

size() should be static if Extent is fill

template <typename ElementType, std::size_t Extent>
class span{ ...
// This function...
constexpr index_type size() const noexcept { return storage_.size; }
};

May be std::enable_if... ?

Support for construction using std::initializer_list<T>

In a lot of my projects I tend to use initializer lists as a shorthand over creating an array object and constructing a span from that. For example:

#include <cstdio>
#include <span>

void UseSpan(std::span<const char> str)
{
    printf("%s\n", str.data());
}

int main() {
  const char arr[] = { 'a', 'b', 'c', 'd', '\0'};
  UseSpan(arr);
  // Error: '<brace-enclosed initializer list>' to 
  //        'std::span<const char>'
  // UseSpan({ 'a', 'b', 'c', 'd', '\0'});  
  return 0;
}

https://godbolt.org/z/tMKbYF

The example is esoteric, but it demonstrates the desired effect. Any particular reason why the span library could not have construction from init lists available to it as well?

It is is something that is desirable, I've made a change on my fork to add support for this that I can make a PR for.

MINSIGSTKSZ cannot be used for constexpr variable

Error description

[1/4] Building CXX object test/CMakeFiles/catch_main.dir/catch_main.cpp.o
FAILED: test/CMakeFiles/catch_main.dir/catch_main.cpp.o 
/usr/bin/g++   -Wall -Wextra -pedantic -O3 -DNDEBUG -std=c++11 -MD -MT test/CMakeFiles/catch_main.dir/catch_main.cpp.o -MF test/CMakeFiles/catch_main.dir/catch_main.cpp.o.d -o test/CMakeFiles/catch_main.dir/catch_main.cpp.o -c /home/user/github/span/test/catch_main.cpp
In file included from /usr/include/signal.h:328,
                 from /home/user/github/span/test/catch.hpp:7712,
                 from /home/user/github/span/test/catch_main.cpp:3:
/home/user/github/span/test/catch.hpp:10453:58: error: call to non-‘constexpr’ function ‘long int sysconf(int)’
10453 |     static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
      |                                                          ^~~~~~~~~~~
In file included from /usr/include/x86_64-linux-gnu/bits/sigstksz.h:24,
                 from /usr/include/signal.h:328,
                 from /home/user/github/span/test/catch.hpp:7712,
                 from /home/user/github/span/test/catch_main.cpp:3:
/usr/include/unistd.h:640:17: note: ‘long int sysconf(int)’ declared here
  640 | extern long int sysconf (int __name) __THROW;
      |                 ^~~~~~~
In file included from /home/user/github/span/test/catch_main.cpp:3:
/home/user/github/span/test/catch.hpp:10512:45: error: size of array ‘altStackMem’ is not an integral constant-expression
10512 |     char FatalConditionHandler::altStackMem[sigStackSize] = {};
      |                                             ^~~~~~~~~~~~
ninja: build stopped: subcommand failed.

Compiling information

g++ --version
g++ (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ldd --version
ldd (Ubuntu GLIBC 2.35-0ubuntu3.1) 2.35
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.


Hotfix

catch.hpp:

...
#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )

#include <signal.h>

#undef MINSIGSTKSZ
#define MINSIGSTKSZ 16384

namespace Catch {
...

Useful links

Support use in CUDA device-side code

nVIDIA's CUDA is a popular ecosystem for general-purpose GPU programming. Essentially, in CUDA, you write kernels to be executed on a GPU using a slightly-restricted variant of C++. However, you need to annotate functions which run the the GPU-device-side as __device__. (For constexpr function this can be skipped, but only with a certain compiler flag which shouldn't be relied upon.) Also, any host-side code of the standard C++ library is not usable.

I would like to ask that this span implementation be adapted for use with CUDA. I've done something similar for std::array, although there's a bit of unnecessary boilerplate in my additions there.

Compilation error caused by data() member

I have an interesting problem. The following code:

#include "span.hpp"
#include <vector>

class Test
{
public:
    Test() { }
    Test(const Test&) = default;
    /* explicit */ Test(const tcb::span<const int>&) { } // Making the constructor explicit **doesn't** fix the issue

    Test& operator=(const Test&) = default;
    Test& operator=(const tcb::span<const int>&) { return *this; }

    const std::vector<int>& data() const { return m_data; } // Commenting this member will fix the error

private:
    std::vector<int> m_data;
};

int main()
{
    Test test;
    test = Test();
    return 0;
}

Produces the following error on Visual Studio 2019 16.11.7 (any /std:c++xx setting):

1>span.hpp(271,1): error C2234: 'abstract declarator': arrays of references are illegal
1>ConsoleApplication1.cpp(28): message : see reference to class template instantiation 'tcb::detail::is_container_element_type_compatible<const Test &,const char,void>' being compiled

It also produces a similar error in gcc 9.3.0:

/home/ceztko/span.hpp: In instantiation of ‘struct tcb::detail::is_container_element_type_compatible<Test&, const int, void>’:
/home/ceztko/span.hpp:363:75:   required by substitution of ‘template<class Container, long unsigned int E, typename std::enable_if<(((E == tcb::dynamic_extent) && tcb::detail::is_container<Container, typename std::remove_cv<typename std::remove_reference<_Tp>::type>::type>::value) && tcb::detail::is_container_element_type_compatible<Container&, const int, void>::value), int>::type <anonymous> > constexpr tcb::span<const int>::span(Container&) [with Container = Test; long unsigned int E = 18446744073709551615; typename std::enable_if<(((E == tcb::dynamic_extent) && tcb::detail::is_container<Container, typename std::remove_cv<typename std::remove_reference<_Tp>::type>::type>::value) && tcb::detail::is_container_element_type_compatible<Container&, const int, void>::value), int>::type <anonymous> = <missing>]’
test.c++:23:17:   required from here
/home/ceztko/span.hpp:264:8: error: creating array of ‘std::__remove_pointer_helper<const std::vector<int>&, const std::vector<int>&>::type’ {aka ‘const std::vector<int>&’}
  264 | struct is_container_element_type_compatible<
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  265 |     T, E,
      |     ~~~~~
  266 |     typename std::enable_if<
      |     ~~~~~~~~~~~~~~~~~~~~~~~~
  267 |         !std::is_same<typename std::remove_cv<decltype(
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  268 |                           detail::data(std::declval<T>()))>::type,
      |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  269 |                       void>::value>::type>
      |                       ~~~~~~~~~~~~~~~~~~~~
/home/ceztko/span.hpp: In instantiation of ‘struct tcb::detail::is_container_element_type_compatible<const Test&, const int, void>’:
/home/ceztko/span.hpp:374:75:   required by substitution of ‘template<class Container, long unsigned int E, typename std::enable_if<(((E == tcb::dynamic_extent) && tcb::detail::is_container<Container, typename std::remove_cv<typename std::remove_reference<_Tp>::type>::type>::value) && tcb::detail::is_container_element_type_compatible<const Container&, const int, void>::value), int>::type <anonymous> > constexpr tcb::span<const int>::span(const Container&) [with Container = Test; long unsigned int E = 18446744073709551615; typename std::enable_if<(((E == tcb::dynamic_extent) && tcb::detail::is_container<Container, typename std::remove_cv<typename std::remove_reference<_Tp>::type>::type>::value) && tcb::detail::is_container_element_type_compatible<const Container&, const int, void>::value), int>::type <anonymous> = <missing>]’
test.c++:23:17:   required from here
/home/ceztko/span.hpp:264:8: error: creating array of ‘std::__remove_pointer_helper<const std::vector<int>&, const std::vector<int>&>::type’ {aka ‘const std::vector<int>&’}

If I comment the data() member, the problem fixes itself, there's clearly a clash. A similar code using a supplied span in a recent C++20 compliant compiler doesn't show the issue. Fortunately the problem was easy to workaround in my case but a fix would be very welcome since it was quite annoying to debug and it could happen elsewhere, since a data() member is to be commonly found.

A couple codacy (cppcheck) issues.

Just a FYI. Codacy reports some minor issues.

Constructors with single arguments that aren't marked explicit.

struct member 'is_container::value' is never used.

Good day

Add versioning info in-source and in git

For the sake of dependency management, it is really nice to have official release version numbers so that I can share with coworkers what version I am using.

I would suggest starting to git tag periodically with code changes so that those changes are versioned -- github has a nice way to do this with release docs that will show up on the project https://help.github.com/en/github/administering-a-repository/managing-releases-in-a-repository

Additionally, I would suggest adding something like the following to the source:

#define VERSION_STRING "v1.2.3"
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 3

This would be really helpful! Thanks for the awesome project.

header pollution

span/include/tcb/span.hpp

Lines 133 to 137 in 5d8d366

#ifdef TCB_SPAN_HAVE_STD_BYTE
using byte = std::byte;
#else
using byte = unsigned char;
#endif

The implementation has some usings that put reduced names in enclosing namespace scope. If a user #defines TCB_SPAN_NAMESPACE_NAME to their own, this can have undesired consequences.

A different solution that avoids header pollution - to not define namespace name and use an alias like mylib::span = tcb::span does not really work because there is more than just a span class template, including non-member functions that are rather complex to alias.

I propose to move these usings to some internal namespace, eg TCB_SPAN_NAMESPACE_NAME::detail.

Does not compile with span of bool type

gcc 9.2 trying to replace gsl::span with tcb::span I'm getting compiler errors when the a container is of type std::vector

void array(std::string_view tag, tcb::span value)
...
{
array(tag, tcb::span(val,nbElem),); // gazillions of error are generated here. No issue if the type of the span is integer

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.